What you'll build
A file upload system where users can drag and drop images or documents, see a progress bar, and get the URL of the uploaded file. You'll use AWS S3 pre-signed URLs so files go directly to cloud storage without passing through your server, making it faster and cheaper. When finished, you'll have a reusable upload component with type and size validation that you can integrate into any form.
Why pre-signed URLs
- File goes directly to S3 (doesn't pass through your server)
- Faster and cheaper
- Your server only generates the URL
Step 1: Ask an AI for the implementation
I need file upload to S3 with:
- Pre-signed URLs
- API route that generates the URL
- Upload component with drag & drop
- Progress bar
- Type and size validation
- TypeScript
Give me the complete code.
API Route
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3 = new S3Client({ region: 'us-east-1' })
export async function POST(request: Request) {
const { filename, contentType } = await request.json()
const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: `uploads/${Date.now()}-${filename}`,
ContentType: contentType
})
const url = await getSignedUrl(s3, command, { expiresIn: 3600 })
return Response.json({ url })
}
Client
async function uploadFile(file: File) {
// 1. Get pre-signed URL
const { url } = await fetch('/api/upload', {
method: 'POST',
body: JSON.stringify({ filename: file.name, contentType: file.type })
}).then(r => r.json())
// 2. Upload directly to S3
await fetch(url, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type }
})
}