@contract-kit/provider-storage-s3
v1.0.0
Published
S3-compatible object storage provider for Contract Kit
Maintainers
Readme
@contract-kit/provider-storage-s3
S3-compatible object storage provider for Contract Kit.
The provider installs the app-facing ctx.ports.storage port. Use it for
production object storage on AWS S3, Cloudflare R2, MinIO, Backblaze B2,
DigitalOcean Spaces, or another S3-compatible backend.
Install
bun add @contract-kit/provider-storage-s3Provider setup
import { s3StorageProvider } from "@contract-kit/provider-storage-s3";
import { createServer } from "@contract-kit/server";
const server = await createServer({
ports: basePorts,
providers: [s3StorageProvider],
createContext: ({ ports }) => ({ ports }),
routes,
});Environment variables:
| Variable | Description |
| --- | --- |
| STORAGE_S3_BUCKET | Bucket name. |
| STORAGE_S3_REGION | Region. Defaults to us-east-1. Use auto for Cloudflare R2. |
| STORAGE_S3_ENDPOINT | Optional S3-compatible endpoint. Required for R2, MinIO, Spaces, B2, and similar services. |
| STORAGE_S3_ACCESS_KEY_ID | Optional static access key. |
| STORAGE_S3_SECRET_ACCESS_KEY | Optional static secret key. |
| STORAGE_S3_SESSION_TOKEN | Optional static session token. |
| STORAGE_S3_PUBLIC_BASE_URL | Optional base URL returned by publicUrl(...) for public objects. |
| STORAGE_S3_FORCE_PATH_STYLE | Optional true or false path-style addressing toggle. |
| STORAGE_S3_KEY_PREFIX | Optional prefix for every object key written by this app. |
AWS S3
STORAGE_S3_BUCKET=my-app-assets
STORAGE_S3_REGION=us-east-1
STORAGE_S3_PUBLIC_BASE_URL=https://cdn.example.comWhen credentials are omitted, the AWS SDK uses its normal credential provider chain.
Cloudflare R2
STORAGE_S3_BUCKET=my-app-assets
STORAGE_S3_REGION=auto
STORAGE_S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
STORAGE_S3_ACCESS_KEY_ID=...
STORAGE_S3_SECRET_ACCESS_KEY=...
STORAGE_S3_PUBLIC_BASE_URL=https://assets.example.comR2 is S3-compatible, but not every S3 feature exists on every compatible
service. This provider only relies on object put, get, head, and
delete.
Direct port factory
import { createS3Storage } from "@contract-kit/provider-storage-s3";
const storage = createS3Storage({
bucket: "my-app-assets",
region: "auto",
endpoint: "https://<account-id>.r2.cloudflarestorage.com",
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
publicBaseUrl: "https://assets.example.com",
});The same StoragePort works with local files, memory tests, and
S3-compatible object stores:
await ctx.ports.storage.put("avatars/user_123.png", avatarBytes, {
contentType: "image/png",
visibility: "public",
});
const object = await ctx.ports.storage.get("avatars/user_123.png");
const url = await ctx.ports.storage.publicUrl("avatars/user_123.png");Visibility
visibility is stored as reserved object metadata so publicUrl(...) can
return URLs only for objects written with visibility: "public". The provider
does not set S3 ACLs. Configure bucket policies, R2 public buckets, or a CDN
outside the provider when objects should be publicly reachable.
The reserved metadata key is ck-visibility. It is hidden from
StorageObject.metadata.
Escape hatch
The provider also installs ctx.ports.s3Storage for S3-specific operations that
do not belong in StoragePort:
import { ListObjectsV2Command } from "@aws-sdk/client-s3";
const s3Key = ctx.ports.s3Storage.objectKey("exports/report.csv");
const s3Prefix = ctx.ports.s3Storage.objectPrefix("exports");
await ctx.ports.s3Storage.client.send(
new ListObjectsV2Command({
Bucket: ctx.ports.s3Storage.bucket,
Prefix: s3Prefix,
}),
);Use objectKey(...) when direct S3 calls need to address objects written
through ctx.ports.storage. Use objectPrefix(...) for list operations. Both
helpers apply the configured STORAGE_S3_KEY_PREFIX.
Devtools
When ctx.ports.devtools is installed, the provider records storage
operations under the storage watcher. Events include operation name, key,
bucket, duration, object size, visibility, and whether a lookup hit. Object
bodies and metadata values are never recorded.
License
MIT
