AWS S3 Asset Storage & Pre-Signed Upload API

Server-side S3 integration with pre-signed URL generation for secure, direct browser-to-S3 file uploads. Supports organized folder structure, Zod-validated API route, and client-side upload hook.

Overview

Complete AWS S3 integration for managing builder assets. Files are uploaded directly from the browser to S3 using pre-signed PUT URLs, keeping AWS credentials secure on the server while eliminating server-side file proxying.

Features

S3 Client (lib/s3.ts)

  • Lazy Singleton - S3 client instantiated once and reused across requests
  • Environment Validation - Throws descriptive errors if required env vars are missing
  • CRC32 Checksum Fix - Disabled automatic checksum computation to prevent 403 errors on pre-signed PUT uploads
  • Pre-Signed URL Generation - createPresignedUploadUrl() with configurable expiry (default 5 minutes)
  • Public URL Construction - getPublicAssetUrl() builds virtual-hosted-style S3 URLs
  • Object Tagging - Supports key/value tags for querying and lifecycle management

API Route (/api/upload/presigned-url)

  • Zod Validation - Request body validated with strict schema for fileName, contentType, and folder
  • Allowed MIME Types - image/svg+xml, image/png, image/jpeg
  • Folder Whitelist - 8 organized folders:
    • logos/ - Team logos and graphics
    • brand-library/ - Brand assets
    • library/ - Shared asset library
    • designs/ - Design templates
    • fabrics/ - Fabric textures
    • patterns/ - Pattern files
    • products/ - Product images
    • screenshots/ - Builder screenshots
  • Collision-Free Keys - Timestamp-prefixed, sanitized file names (e.g. logos/1707321600000-team_logo.svg)
  • S3 Object Tags - Each upload tagged with folder, source (builder-upload), and content-type

Client Hook (useS3Upload)

  • Two-Step Flow - Request pre-signed URL, then PUT directly to S3
  • State Management - Exposes isUploading, uploadError, and clearUploadError
  • Error Recovery - Catches and surfaces errors from both the API and S3 upload steps
  • Typed Response - Full TypeScript types for the presigned URL response

Architecture

Browser                    Next.js API              AWS S3
  │                            │                      │
  ├─ POST /presigned-url ─────►│                      │
  │                            ├─ createPresignedUrl ─►│
  │                            │◄─ signed URL ─────────┤
  │◄─ { presignedUrl, publicUrl }                      │
  │                            │                      │
  ├─ PUT file directly ────────────────────────────────►│
  │◄─ 200 OK ─────────────────────────────────────────┤
  │                            │                      │
  └─ Store publicUrl in form   │                      │

Environment Variables

  • AWS_S3_BUCKET_NAME - S3 bucket name (e.g. asb-sports-assets)
  • AWS_S3_REGION - AWS region (e.g. eu-central-1)
  • AWS_S3_ACCESS_KEY_ID - IAM access key for S3 operations
  • AWS_S3_SECRET_ACCESS_KEY - IAM secret key for S3 operations

Dependencies Added

  • @aws-sdk/client-s3 - AWS S3 client
  • @aws-sdk/s3-request-presigner - Pre-signed URL generation