Bun S3
AWS S3 (and any S3-compatible bucket) via Bun's native Bun.S3Client instead of @aws-sdk/client-s3. Bun-only.
Installation
This adapter has no extra peer dependencies, but it requires the Bun runtime - it’s built on Bun’s native Bun.S3Client, which Node doesn’t provide. (Outside Bun it throws unless you hand it a Bun.S3Client-shaped client yourself.)
npm install files-sdkpnpm add files-sdkyarn add files-sdkbun add files-sdkUsage
import { Files } from "files-sdk";
import { bunS3 } from "files-sdk/bun-s3";
const files = new Files({
adapter: bunS3({
bucket: "uploads",
region: "us-east-1",
// accessKeyId / secretAccessKey auto-loaded by Bun from
// S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY (or AWS_* equivalents)
}),
});
// Or hand it the singleton Bun.s3 client directly:
new Files({ adapter: bunS3({ client: Bun.s3 }) });
Three things differ from files-sdk/s3: copy() streams bytes through your process because Bun doesn’t expose a server-side CopyObject primitive; upload() throws on metadata and cacheControl because Bun.S3Client.write() has no equivalent options; and signedUploadUrl() throws on maxSize because Bun exposes presigned URLs only - not S3 POST policy fields. Reach for files-sdk/s3 on the same bucket when you need any of those.
Options
client?BunS3ClientLike
A pre-configured `Bun.S3Client`-shaped instance — for example the global `Bun.s3`, or one constructed with specific credentials elsewhere in your app. When set, the adapter uses it as-is and rejects any of `bucket`, `region`, `endpoint`, `virtualHostedStyle`, `accessKeyId`, `secretAccessKey`, `sessionToken` at construction (they would be silently ignored otherwise). When unset, the adapter constructs its own client from the options below.
BunS3ClientLikebucket?string
S3 bucket name. Scopes operations and is exposed as `adapter.bucket`. Falls back to `S3_BUCKET` / `AWS_BUCKET` via Bun's built-in resolution.
stringregion?string
AWS region (e.g. `us-east-1`). Falls back to `S3_REGION` / `AWS_REGION` via Bun's resolution.
stringendpoint?string
Override the S3 service endpoint. Use this to point at S3-compatible services (R2, DigitalOcean Spaces, Wasabi, MinIO, ...).
stringvirtualHostedStyle?boolean
Use virtual-hosted-style addressing (`https://<bucket>.<endpoint>`) instead of path-style. Defaults to `false` — flip on for endpoints that require it.
booleanaccessKeyId?string
Static access key ID. Skip to let Bun resolve it from `S3_ACCESS_KEY_ID` / `AWS_ACCESS_KEY_ID`.
stringsecretAccessKey?string
Static secret access key. Skip to let Bun resolve it from `S3_SECRET_ACCESS_KEY` / `AWS_SECRET_ACCESS_KEY`.
stringsessionToken?string
Static session token for temporary credentials. Skip to let Bun resolve it from `S3_SESSION_TOKEN` / `AWS_SESSION_TOKEN`.
stringpublicBaseUrl?string
Origin used to build URLs from `url()`. When set, `url(key)` returns `${publicBaseUrl}/${key}` and skips signing — use this if your bucket is fronted by a CDN or has a public-read policy. Passing `responseContentDisposition` still forces a signed URL even when this is set, because a permanent CDN URL has no signature in which to bind the override. When unset, `url()` returns a presigned GetObject (1-hour default).
stringdefaultUrlExpiresIn?number
Default expiry, in seconds, for the presigned URLs returned by `url()` when `publicBaseUrl` isn't set. Defaults to 3600 (1 hour). Per-call `url(key, { expiresIn })` overrides.
numberCompatibility
| Method | Status | Notes |
|---|---|---|
upload |
⚠️ | User metadata and cacheControl throw - Bun.S3Client.write() exposes neither field. Reach for s3() on the same bucket if you need them. Stream bodies are wrapped in a Response and handed to Bun’s writer. Pause/resume via control is in-process only — Bun’s S3 client exposes no resumable upload id, so chunks are buffered and a token cannot resume in a new process. |
download |
✅ | |
delete |
✅ | |
list |
✅ | |
search |
✅ | |
head |
✅ | |
exists |
✅ | |
copy |
⚠️ | Client-side stream copy - Bun.S3Client doesn’t expose a server-side CopyObject, so the source is streamed through this process and re-uploaded. Doubled bandwidth, not atomic, and drops Content-Disposition/cache headers/user metadata/ACL (only Content-Type is preserved). Reach for s3() on the same bucket for server-side copy. |
url |
✅ | |
signedUploadUrl |
⚠️ | PUT URL only - Bun exposes presigned URLs, not S3 POST policy fields, so maxSize throws (no content-length-range policy). Enforce upload caps at your application gateway instead. |