Problem Statement
Lots of apps need to upload and serve files but shouldn't stream the bytes through their own backend (memory pressure, scaling, coupling). The clean pattern is presigned URLs — the client uploads/downloads directly to/from object storage — but every project re-implements the boilerplate (key generation, presigning, expiry, S3 client wiring). And teams want it to work against any S3-compatible provider, not just AWS.
Proposed Solution
A tiny, stateless Go + Gin service that sits between an app and any S3-compatible storage and exposes just two endpoints: one to get a presigned PUT URL (with a server-generated UUID key), and one to get a presigned GET URL for an existing key. No database, no file bytes ever touching the service — it only mints URLs.
Full Solution Details
GET /get-upload-uri— generates a server-side UUID key and returns a presigned S3 PUT URL valid 15 minutes; optional 5-charprefix/suffixquery params decorate the key (e.g.imgs1-<uuid>-thumb1).GET /get-file-uri?key=...— returns a presigned S3 GET URL for an existing key, valid 1 hour.GET /health— status.- Provider-agnostic — works with Tigris / t3.dev (region
auto) and AWS S3 via env config (S3_ENDPOINT,S3_REGION,S3_BUCKET, keys). - Observability — every request logs running counters (
total_upload_uris,total_file_uris) and memory footprint (alloc,goroutines), plus a periodic 60s memory snapshot (alloc/sys/gc_cycles/goroutines).
Technical Documentation
Go + Gin, using the AWS SDK's presigner against an S3-compatible endpoint. The service is deliberately stateless and database-free: keys are UUIDs generated at request time (optionally affixed), and clients hold the keys — so there's nothing to persist. Two distinct expiry windows (15m for uploads, 1h for reads) match the typical lifecycle. A notable touch is the built-in memory/goroutine logging on every request and on a 60s timer, giving lightweight, dependency-free observability into the process's footprint — fitting for a service meant to run cheap and small.
Tech Stack
Go, Gin, AWS SDK presigning; S3-compatible storage (Tigris / t3.dev / AWS S3); env-based configuration.
System Design
App ──GET /get-upload-uri (prefix?/suffix?)──► Go file-service
◄── { key: <prefix>-<uuid>-<suffix>, uri: presigned PUT (15m) }
Client ──PUT bytes directly──► S3-compatible bucket (Tigris/t3.dev/AWS)
App ──GET /get-file-uri?key=...──► Go file-service
◄── { uri: presigned GET (1h) }
Client ──GET bytes directly──► bucket
Stateless · no DB · bytes never touch the service
Logs: per-request counters + memory footprint · 60s memory snapshot
Smart Architectural Decisions
- Bytes never touch the service. By only minting presigned URLs (clients upload/download directly to S3), the service stays tiny, cheap, and infinitely scalable — the textbook-correct pattern, implemented as a reusable primitive.
- Stateless, no database. Server-generated UUID keys held by the client mean there's nothing to store — radically simple to deploy and operate.
- Provider-agnostic via endpoint config. Targeting any S3-compatible endpoint (region
autofor Tigris/t3.dev) avoids AWS lock-in and makes it a drop-in for cheaper storage. - Two expiry windows by intent (15m upload / 1h read) encode the real lifecycle rather than a one-size TTL.
- Built-in memory/goroutine logging gives free observability — a thoughtful touch for a small Go service where you want to confirm the footprint stays tiny.
Impacts
A reusable, language-agnostic file-upload primitive: any app can offload uploads/downloads to object storage through two endpoints, with no DB and a tiny memory footprint. It's the same stateless-files pattern the author reuses across WorkSight, Pracket, and Dondie — extracted into a standalone Go service.
Demonstrated Skills
Go + Gin microservice design; S3 presigned-URL pattern and provider-agnostic storage integration; stateless service architecture (no DB); pragmatic observability (memory/goroutine/counter logging); env-driven 12-factor configuration; recognizing and extracting a cross-cutting concern into a reusable service.