Cloudflare R2
Cloudflare R2 over the S3-compatible HTTP API. Auto-loads R2_* env vars or accepts an R2Bucket binding inside Workers.
Installation
@aws-sdk/client-s3, @aws-sdk/s3-presigned-post, and @aws-sdk/s3-request-presigner are optional peer dependencies of files-sdk - install alongside the SDK so the adapter's imports resolve at runtime.
Cloudflare R2 over the S3-compatible HTTP API. Auto-loads from R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY. Inside Cloudflare Workers you can pass an R2Bucket binding directly instead.
import { Files } from "files-sdk";import { r2 } from "files-sdk/r2";const files = new Files({ adapter: r2({ bucket: "uploads", accountId: process.env.R2_ACCOUNT_ID!, // accessKeyId / secretAccessKey auto-loaded // from R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY }),});publicBaseUrl - optional, an r2.dev subdomain or custom domain bound to the bucket. When set, url() returns `${publicBaseUrl}/${key}` and skips signing.
Hybrid: binding + HTTP credentials
Inside a Worker, you can pass both a binding and HTTP credentials. Reads and writes go through the binding (no egress, no extra round trip); url() and signedUploadUrl() route through the HTTP signer because a Worker binding has no signing primitive. The S3 client is lazy-loaded - bindings-only Workers don't pull @aws-sdk/client-s3 into their bundle.
// Inside a Cloudflare Worker. The binding handles uploads/downloads// (intra-Worker, no egress fees). The HTTP credentials let url() and// signedUploadUrl() sign presigned URLs the binding alone can't produce.const files = new Files({ adapter: r2({ binding: env.UPLOADS, bucket: "uploads", accountId: env.R2_ACCOUNT_ID, accessKeyId: env.R2_ACCESS_KEY_ID, secretAccessKey: env.R2_SECRET_ACCESS_KEY, }),});