Prerequisites
- AWS account with S3 access
- IAM user with S3 permissions
1. Create S3 Bucket
- Go to AWS S3 Console
- Click Create bucket
- Configure:
- Bucket name:
your-app-uploads(must be globally unique) - Region: Choose closest to your users
- Block Public Access: Keep enabled (we use presigned URLs)
- Bucket name:
- Click Create bucket
CORS Configuration
Add this CORS policy to your bucket:The
ExposeHeaders: ["ETag"] is required for multipart uploads to work
correctly.2. Create IAM User
- Go to IAM Console
- Create a new user with Programmatic access
- Attach this policy:
- Save the Access Key ID and Secret Access Key
3. Environment Variables
Add these to your backend.env:
4. API Endpoints
The upload router (apps/api/src/routers/upload.ts) provides these endpoints:
Simple Upload
| Endpoint | Description |
|---|---|
requestUploadUrl | Get presigned URL for direct upload |
confirmUpload | Mark upload as complete |
Multipart Upload
| Endpoint | Description |
|---|---|
initiateMultipart | Start multipart upload, get upload ID |
getPartUrl | Get presigned URL for a specific part |
completePart | Record completed part with ETag |
completeMultipart | Finalize multipart upload |
abortMultipart | Cancel and cleanup failed upload |
5. Database Schema
TheFile model tracks uploads:
6. Storage Quota
Configure per-user storage limits inupload.ts:
Security Considerations
Presigned URLs
Presigned URLs
URLs expire after 1 hour by default. Adjust
expiresIn in s3.ts if
needed.File Type Validation
File Type Validation
Allowed MIME types are configured in
ALLOWED_MIME_TYPES. Add/remove as
needed.Rate Limiting
Rate Limiting
Consider adding rate limiting to upload endpoints in production.
Virus Scanning
Virus Scanning
For user-generated content, consider AWS Lambda + ClamAV for scanning.
Test Checklist
- API starts without S3 errors
requestUploadUrlreturns a presigned URL- Uploaded file can be confirmed in the database
Troubleshooting
If uploads fail, re-check S3 credentials and bucket CORS settings.Remove / Disable
To disable uploads while you configure S3, set:apps/mobile/features/feature-registry.tsx → featureFlags.fileUploads = false
For production removal guidance, see Removing Features.