Providers
Configure Amazon S3, Cloudflare R2, MinIO, and other S3-compatible storage.
dimah-s3 talks to storage through the AWS SDK S3Client. Any service that
speaks enough of the S3 API can work — Amazon S3, Cloudflare R2, MinIO, and
similar backends.
S3-compatible is not the same as Amazon S3. Providers skip or ignore parts of the API. With a presign-first stack, those gaps show up quickly: the browser hits storage directly, so CORS, addressing, and the upload method have to match what the provider actually supports.
| Topic | Why it matters |
|---|---|
| Presigned POST vs PUT | Default upload is POST. Some providers only accept PUT. |
| Object ACL | acl: "public-read" needs real ACL support. Others use bucket policies or a public-access toggle. |
| Endpoint & addressing | Custom endpoints often need endpoint, and sometimes forcePathStyle: true. |
| CORS | Browser uploads hit storage directly. The bucket must allow your origin, methods, and headers. |
| Public URLs | Public reads may use a CDN, a custom domain, or *.r2.dev — not object ACL. |
Wire the shared client once in Quickstart. Use the guides below only for the extras that defaults do not cover.
Comparison
| AWS S3 | Cloudflare R2 | MinIO | |
|---|---|---|---|
| Presigned POST | Yes | No | Yes |
| Presigned PUT | Yes | Yes (required) | Yes |
| Object ACL | Yes | Ignored | Use policies |
Custom endpoint | Optional | Required | Required |
forcePathStyle | Usually off | Off | Required |
Configuration
import { dimahS3, route } from "@dimah-s3/server";
export const s3 = dimahS3({
client: awsS3,
bucket: process.env.S3_BUCKET!,
routes: {
avatar: route({
upload: { method: "PUT" },
}),
},
});The default upload method is POST. Switch to PUT when the provider has no
Presigned POST (R2 is the usual case). ACL on confirm comes from
upload.acl / object — there is no GetObjectAcl lookup.
Client hooks and @dimah-s3/ui read method from the presign response.
Change it on the server only.