S3 file storage
The boilerplate includes a complete S3 file storage implementation that works with AWS S3, Cloudflare R2, MinIO, and other S3-compatible services. It provides secure file uploads and downloads using signed URLs.
Environment variables
Configure your S3-compatible storage provider in .env:
# S3-compatible storage
S3_ENDPOINT=https://your-s3-endpoint.com
S3_REGION=auto
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_BUCKET=your-bucket-name
Provider examples
AWS S3:
S3_ENDPOINT=https://s3.amazonaws.com
S3_REGION=us-east-1
Cloudflare R2:
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_REGION=auto
MinIO:
S3_ENDPOINT=https://minio.yourdomain.com
S3_REGION=us-east-1
Service functions
The implementation consists of three layers of services:
Server-side services
server/services/storage-server-service.ts - High-level storage operations:
// Get signed URL for uploading a file
await getSignedUploadUrl(key, contentType, bucket?)
// Get signed URL for downloading a file
await getSignedDownloadUrl(key, expiresIn?, filename?, bucket?)
server/utils/signed-url.ts - Low-level signed URL generation:
// Generate signed upload URL (default: 2 minutes expiry)
await getSignedUploadUrl(s3Client, bucket, key, contentType, expiresIn?)
// Generate signed download URL (default: 1 hour expiry)
await getSignedDownloadUrl(s3Client, bucket, key, expiresIn?, filename?)
Client-side services
app/services/storage-client-service.ts - Client API for file operations:
// Upload file with progress tracking
await uploadFileWithSignedUrl(file, contentType, onProgress?, customKey?)
// Download file (triggers browser download)
await downloadFileWithSignedUrl(key, filename?)
// Generate unique storage key
generateStorageKey(filename?)
Signed URLs vs. public URLs
Signed URLs (default)
The boilerplate uses signed URLs for secure, temporary access:
- Secure - No public access to files
- Temporary - URLs expire after a set time (configurable)
- Access control - Server validates permissions before generating URLs
- Private buckets - Bucket remains private, files not publicly accessible
Upload flow:
- Client requests signed upload URL from server
- Server generates temporary URL with write permissions
- Client uploads directly to S3 using signed URL
- Client saves file metadata to database
Download flow:
- Client requests signed download URL from server
- Server validates user permissions
- Server generates temporary URL with read permissions
- Client downloads file using signed URL
Public URLs
For public files (avatars, product images, etc.), you can use public buckets or CDN URLs:
const publicUrl = `https://cdn.yourdomain.com/${key}`
Public URLs don't require server-side URL generation but sacrifice access control.
Database metadata
Files are tracked in the database with the File model:
model File {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
key String @unique
name String
mimeType String
size BigInt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("file")
}
Key points:
keyis the S3 object key (unique identifier in bucket)sizeuses BigInt for large files- User relationship for access control
- Metadata is searchable via Prisma queries
Usage example
Upload a file
import { uploadFileWithSignedUrl } from '@/services/storage-client-service'
import { createFile } from '@/services/files-client-service'
const file = event.target.files[0]
// Upload to S3
const { key } = await uploadFileWithSignedUrl(
file,
file.type,
(progress) => console.log(`${progress}% uploaded`)
)
// Save metadata
await createFile({
key,
name: file.name,
size: file.size,
mimeType: file.type,
userId: currentUser.id,
})
Download a file
import { downloadFileWithSignedUrl } from '@/services/storage-client-service'
// Triggers browser download
await downloadFileWithSignedUrl(file.key, file.name)
Complete implementation
For a full implementation example with drag-and-drop uploads, file tables, and metadata management, see the S3 file upload/download template.