1. Overview
This document describes my fully headless, serverless image upload architecture, built on top of AWS services. The goal was to provide an API-driven approach—with no dedicated frontend—so other applications or clients can easily integrate and upload images while adhering to specific file and quota constraints.
I used API Gateway, Lambda, S3, and DynamoDB to implement a flow that manages user usage limits, verifies uploaded files, and ensures each upload is appropriately tracked. Although the system started as a simple MVP (Minimum Viable Product), I have expanded it to handle file validation, user quotas, and more advanced features.
2. Core Architecture: The MVP
2.1 Basic Flow
- API Gateway + Lambda
- The API endpoint receives a request to generate a pre-signed URL for S3
- Lambda performs initial checks (e.g., user identification) and responds with the pre-signed URL
- Amazon S3
- The client directly uploads images to S3 using the pre-signed URL
- This avoids passing file data through Lambda, reducing cost and complexity
- DynamoDB (Optional at the MVP stage)
- Stores metadata about each file (filename, timestamp, etc.)
- Initially, it's possible to skip DynamoDB and rely solely on S3, but including metadata leads to more sophisticated control down the road
- Download Flow
- Typically, my architecture reuses the same Lambda logic to generate a download pre-signed URL, or potentially uses a CDN
Why pre-signed URL?
- It allows uploads directly to S3 without routing large files through the server
- It provides time-limited access and thereby improve security
- Uploaded media cannot be crawled by a random public crawler
3. File Type & Size Validation
3.1 The Need for Validation
By default, a pre-signed URL lets clients upload practically anything. I introduced validation to restrict file size or type (e.g., max 10 MB, only JPEG). Since the serverless Lambda doesn't see file contents during upload, I had to design a separate verification step.
3.2 Two-Stage Verification (Pending and Confirm)
- Requesting a pre-signed URL
- When a client requests an upload link, they include metadata like
fileName
, fileSizeInByte
, and optionally content type
- The Lambda checks these fields and, if acceptable, stores a record in DynamoDB with a PENDING status
- A pre-signed URL is generated and returned
- Confirming the Upload
- After the client finishes uploading to S3, they call the "notifyUploadComplete" API
- Lambda inspects the actual object in S3 via
HeadObjectCommand
to compare size, content type, etc.
- If valid, the record is moved to CompletedUploads; otherwise, the file is deleted from S3
This ensures files that don't match the declared metadata are removed.