← all projects
Go Microservice

Go File Service

Lightweight Go file proxy service. Generates presigned upload and view URLs for any S3-compatible storage (Tigris, t3.dev, AWS S3) — two endpoints, zero database, with built-in memory and activity logging.

Stateless Go + Gin glue between any app and S3-compatible object storage: server-generated UUID keys, presigned PUT (15m) and GET (1h) URLs, and per-request memory/counter logging — no database, tiny footprint.

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-char prefix/suffix query 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 auto for 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.

Notes

  • Demonstrates the right pattern, cleanly: presigned-URL uploads (bytes never hit the service) is exactly how scalable file handling should work — and he extracted it into a tiny, reusable Go service rather than re-coding it everywhere.
  • Go competency + minimalism: a stateless, DB-free, two-endpoint service shows he values simplicity and knows Go/Gin and the AWS SDK.
  • Provider-agnostic (Tigris/t3.dev/AWS) shows cost-awareness and avoidance of vendor lock-in.
  • Observability instinct: baking in memory/goroutine logging on a small service is a nice ops-minded detail.
  • Architectural maturity: recognizing file-upload as a cross-cutting concern and factoring it out (then consuming it from WorkSight/Pracket/Dondie) is real systems thinking.
Ask me anything