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:
- The image appears in the UploadThing file browser.
- The returned URL is what's stored in
users.imageUrl. - The
Cache-Controlheader on the served URL is immutable.
Where it lives in the codebase
apps/server/src/modules/storage/application/ports/file-storage.ts— the portapps/server/src/modules/storage/infrastructure/uploadthing/— the adapterapps/server/src/modules/storage/infrastructure/di.ts— provider switch
Related
Sentry
Wire Sentry as the error reporter for both the Bun + Express server and the Next.js client. Server uses @sentry/bun, client uses @sentry/nextjs.
Resend
Send transactional email (auth, billing, invitations) through Resend — wired to the notifications module's mailer port. Drop-in replacement for SMTP / SendGrid / Postmark.