s3mini
Last reviewed: about 16 hours ago
You must generate an Access Key before getting started. All examples will utilize access_key_id and access_key_secret variables which represent the Access Key ID and Secret Access Key values you generated.
s3mini ↗ is a zero-dependency, lightweight (~20 KB minified) TypeScript S3 client that uses AWS SigV4 signing. It runs natively on Node.js, Bun, and Cloudflare Workers without polyfills.
Unlike the AWS SDKs, s3mini expects a bucket-scoped endpoint — the bucket name is part of the endpoint URL, so you do not pass a separate bucket parameter to each operation.
npm install s3miniimport { S3mini } from "s3mini";
const s3 = new S3mini({ accessKeyId: process.env.R2_ACCESS_KEY_ID!, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!, // Bucket-scoped endpoint — include your bucket name in the path endpoint: `https://${process.env.ACCOUNT_ID}.r2.cloudflarestorage.com/my-bucket`, region: "auto",});
// Upload an objectawait s3.putObject("hello.txt", "Hello from s3mini!");
// Download an object as a stringconst text = await s3.getObject("hello.txt");console.log(text);
// List objects with a prefixconst objects = await s3.listObjects("/", "hello");console.log(objects);
// Delete an objectawait s3.deleteObject("hello.txt");s3mini works natively in Workers without the nodejs_compat compatibility flag.
import { S3mini } from "s3mini";
interface Env { R2_ACCESS_KEY_ID: string; R2_SECRET_ACCESS_KEY: string; ACCOUNT_ID: string;}
export default { async fetch(request: Request, env: Env): Promise<Response> { const s3 = new S3mini({ accessKeyId: env.R2_ACCESS_KEY_ID, secretAccessKey: env.R2_SECRET_ACCESS_KEY, endpoint: `https://${env.ACCOUNT_ID}.r2.cloudflarestorage.com/my-bucket`, region: "auto", });
const url = new URL(request.url); const key = url.pathname.slice(1); // strip leading "/"
if (!key) { return new Response("Missing object key", { status: 400 }); }
switch (request.method) { case "PUT": { const data = await request.arrayBuffer(); const contentType = request.headers.get("content-type") ?? "application/octet-stream"; await s3.putObject(key, new Uint8Array(data), contentType); return new Response("Created", { status: 201 }); }
case "GET": { const response = await s3.getObjectResponse(key); if (!response) { return new Response("Not Found", { status: 404 }); } return new Response(response.body, { headers: { "content-type": response.headers.get("content-type") ?? "application/octet-stream", etag: response.headers.get("etag") ?? "", }, }); }
case "DELETE": { await s3.deleteObject(key); return new Response(null, { status: 204 }); }
default: return new Response("Method Not Allowed", { status: 405 }); } },};Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2026 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-