S3
Configure the built-in S3 storage adapter: client config, ACLs, object metadata, and peer dependencies.
s3Adapter from @upload-stuff/server/adapters/s3 is a StorageAdapter
factory backed by the AWS SDK. Pass it straight to storageAdapter inside
UploadStuff(...) — the instance calls the factory with its resolved types:
import { s3Adapter } from "@upload-stuff/server/adapters/s3";
storageAdapter: s3Adapter({
config: {
region: "us-east-1",
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
},
bucket: process.env.S3_BUCKET!,
}),config accepts the full S3ClientConfig from @aws-sdk/client-s3.
ACLs
The adapter sets an ACL on every upload: public-read when the route's
isPublic is true, private otherwise.
Your bucket must allow ACLs
S3's default Object Ownership setting ("bucket owner enforced") rejects any
request that carries an ACL, so presigned PUTs fail. Set Object Ownership to
"bucket owner preferred" (ACLs enabled) for buckets used with this adapter.
Object metadata
objectMetadata is optional and typed against the instance's declared
fields — keep the adapter inline in UploadStuff(...) so that inference
works:
storageAdapter: s3Adapter({
// ...
objectMetadata: (file) => ({ owner: file.userId ?? "" }),
}),The adapter signs the resolved values as x-amz-meta-* request headers (kept
out of the presigned URL) and the client replays them on the PUT. Object
metadata is returned on every GetObject — on public buckets, avoid values you
don't intend to expose (a user id, for example).
Peer dependencies
@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner (>=3.700.0) are
optional peer dependencies — install them only if you use this adapter, so
projects on another storage backend don't pull in the AWS SDK:
pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner