Storage · modules/storage

Production-ready adapter

S3-compatible
object storage.

The storage module owns file uploads behind an IStorageProvider port. The S3 adapter runs on the AWS SDK and — because AWS, Cloudflare R2 and MinIO speak the same protocol — the same code serves all three, differing only in a few env vars.

AWS, R2 or MinIO — same adapter

The only differences between AWS S3, Cloudflare R2 and MinIO are the endpoint, path-style addressing, and the public base URL — all driven by env. Point the same S3StorageProvider at any of them.

apps/server/.env
 1  STORAGE_PROVIDER=s3
 2  STORAGE_S3_BUCKET=my-bucket
 3  STORAGE_S3_REGION=auto
 4  STORAGE_S3_ACCESS_KEY_ID=...
 5  STORAGE_S3_SECRET_ACCESS_KEY=...
 6  # R2 / MinIO only:
 7  STORAGE_S3_ENDPOINT=https://<accountid>.r2.cloudflarestorage.com
 8  STORAGE_S3_FORCE_PATH_STYLE=true   # MinIO

What ships

Presigned URLs

Uploads and downloads use presigned URLs via @aws-sdk/s3-request-presigner — bytes never proxy through your server.

Content-addressed keys

Avatar keys are avatars/{userId}-{sha256:16}.{ext}, so the immutable one-year cache header is safe across re-uploads — a new file writes a new key.

One port, five providers

STORAGE_PROVIDER selects null / s3 / local / uploadthing / supabase behind the same IStorageProvider interface.

Public or signed

Set a public base URL (CDN / R2 public bucket) or let getPublicUrl fall back to a long-lived signed URL.

Configuration reference

Environment variables

  • STORAGE_PROVIDERrequired

    Set to s3

  • STORAGE_S3_BUCKETrequired
  • STORAGE_S3_REGIONrequired
  • STORAGE_S3_ACCESS_KEY_IDrequired
  • STORAGE_S3_SECRET_ACCESS_KEYrequired
  • STORAGE_S3_ENDPOINToptional

    R2 / MinIO / CEPH endpoint

  • STORAGE_S3_FORCE_PATH_STYLEoptional

    true for MinIO

Where it lives

  • apps/server/src/modules/storage/infrastructure/providers/s3-storage-provider.ts
  • apps/server/src/modules/storage/infrastructure/providers/index.ts

One storage adapter, any S3 backend.

Ship uploads on AWS S3, Cloudflare R2 or MinIO without touching your application code.