SaaS Starter
Integrations

UploadThing

Use UploadThing as a managed object-storage backend for the storage module without changing any application code — the storage port stays the same.

Status: scaffold. Fill in the sections below before publishing.

What you get

The modules/storage bounded context exposes a single port: IFileStorage. UseDeploy ships three adapters out of the box — Local, S3, and UploadThing — and the active provider is chosen by env var. Application code (avatar uploads, attachment downloads) is identical regardless of provider.

Avatars use content-addressed keys (avatars/{userId}-{sha256:16}.{ext}) so the immutable Cache-Control header is safe across re-uploads — this is true for every adapter.

Setup

1. Create the UploadThing app

In the UploadThing dashboard, create a project. Note the app ID, secret, and the App URL for the project.

2. Set env vars

# apps/server/.env
STORAGE_PROVIDER=uploadthing
UPLOADTHING_TOKEN=...
UPLOADTHING_APP_ID=...

The provider switches at boot via the storage container (infrastructure/di). With STORAGE_PROVIDER unset or local, UploadThing is a no-op even if the keys are present.

3. Configure file routes

UploadThing routes are declared centrally in apps/server/src/modules/storage/infrastructure/uploadthing/routes.ts. Each route maps a logical use case (avatar, attachment) to size + MIME constraints + the auth check that runs before the upload URL is signed.

4. Smoke test

Upload an avatar through the dashboard /settings/profile page and confirm:

  1. The image appears in the UploadThing file browser.
  2. The returned URL is what's stored in users.imageUrl.
  3. The Cache-Control header on the served URL is immutable.

Where it lives in the codebase

  • apps/server/src/modules/storage/application/ports/file-storage.ts — the port
  • apps/server/src/modules/storage/infrastructure/uploadthing/ — the adapter
  • apps/server/src/modules/storage/infrastructure/di.ts — provider switch

On this page