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 PUT vs POST | Default upload is PUT. POST is opt-in, and some providers have no presigned POST. |
| SDK checksums | The S3Client must set requestChecksumCalculation: "WHEN_REQUIRED", or presigned uploads fail. |
| 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 PUT | Yes (default) | Yes (default) | Yes (default) |
| Presigned POST | Yes | No | Yes |
| Object ACL | Yes | Ignored | Use policies |
Custom endpoint | Optional | Required | Required |
forcePathStyle | Usually off | Off | Typically on |
Configuration
import { S3Client } from "@aws-sdk/client-s3";
import { dimahS3, route } from "@dimah-s3/server";
export const awsS3 = new S3Client({
// region, endpoint, credentials …
requestChecksumCalculation: "WHEN_REQUIRED",
});
export const s3 = dimahS3({
client: awsS3,
bucket: process.env.S3_BUCKET!,
routes: {
avatar: route({
upload: { method: "POST" },
}),
},
});The default upload method is PUT, which every provider here supports. Switch
to POST only when you want a form upload and the provider has presigned POST.
Confirm reports the ACL from upload.acl or object; there is no
GetObjectAcl lookup.
Client hooks and @dimah-s3/ui read method from the presign response.
Change it on the server only.