Eleven modules that take you from an empty Git repo to a scalable, resilient image service — handling uploads, validation, async processing, multiple formats and sizes, CDN distribution, signed URLs, and edge cases. This track covers blob storage, async pipelines, media processing, caching, and operational excellence for user-generated content at scale.
← Back to Learning TracksStart with a clear design. Image services are deceptively complex — edge cases, security, and performance are everywhere.
.editorconfig, .gitignore, linter/formatter).images (metadata), variants (sizes/formats), uploads (user tracking).git clone + one command spins up the server./docs/design.md with schema sketch.The baseline: accept uploads, store them durably, and serve them back. Start with your storage backend.
POST /upload: multipart file upload, store in blob storage, record metadata in DB.GET /images/:id: fetch from blob storage, return with correct content-type.DELETE /images/:id: delete from blob storage and DB.Content-Type, ETag, Cache-Control.curl -F file=@test.jpg POST /upload stores the file and returns an image ID.GET /images/:id returns the file with correct content-type.Users can upload anything. Validate format, size, content. Prevent zip bombs, SSRF, path traversal.
.jpg extension is rejected.Resizing images is expensive. Don't block the upload. Enqueue, process asynchronously, store variants.
GET /images/:id?size=thumb blocks until the thumbnail is ready.< 5 seconds on local hardware.Different clients need different sizes. Generate: thumbnail (200x200), medium (800x800), full (original). Support modern formats: WebP, AVIF.
GET /images/:id?size=medium&format=webp returns WebP, falling back to JPEG.GET /images/:id?size=thumb returns a 200x200 thumbnail.GET /images/:id?format=webp returns WebP if available, else original.EXIF data can leak sensitive info (GPS, camera, timestamps). Extract what's useful, strip what's not.
images table.GET /images/:id/metadata with safe EXIF (omit GPS, serial).< 100x100).GET /images/:id/metadata returns EXIF data (no GPS or sensitive info).Serve images from edge locations, not your origin. Integrate with a CDN: CloudFlare, Fastly, or AWS CloudFront.
Cache-Control: public, max-age=31536000 for immutable images.POST /images/:id/purge to purge from CDN if an image is deleted.https://cdn.example.com/images/:id.CF-Cache-Status or equivalent).Not all images are public. Generate time-limited signed URLs for private uploads. Implement ownership and sharing.
public flag to images. Private images are only accessible via signed URLs.POST /images/:id/signed-url: generate a time-limited signed URL (valid for 1 hour, configurable).GET /images/:id.POST /images/:id/share to grant time-limited access to another user.POST /images/:id/signed-url generates a URL valid for 1 hour.You can't debug what you can't see. Instrument: logs, metrics, traces. Monitor upload latency, processing time, CDN performance.
Edge cases are everywhere: corrupted uploads, missing processing, quota exceeded. Test them all.
Ship it. Deploy, document, set up monitoring, go live.
docker-compose.yml for local dev (app + Postgres + worker + MinIO).main deploys to production automatically./docs.