Snippetbox
A server-rendered Go web app for sharing text snippets, with sessions, CSRF protection, MySQL persistence, and tests around the HTTP layer.
- Year
- 2026
- Role
- Backend & HTTP fundamentals
- Stack
Snippetbox is a small web application where users can sign up, log in, and share text snippets that expire after a set number of days. This project follows the structure of Alex Edwards’ Let’s Go, which I used to learn how a Go web application fits together without a framework.
The feature set is deliberately small. The learning was in the details around it.
What it does
- Shows the ten most recent snippets on the home page.
- Lets authenticated users create snippets with an expiry date.
- Handles signup, login, and logout with hashed passwords.
- Stores users and snippets in MySQL.
- Protects state-changing requests with CSRF tokens.
- Serves templates and static files from an embedded filesystem.
How it is put together
The application uses Go’s standard net/http package with httprouter for routes and alice for middleware chaining. Dependencies are passed explicitly into an application struct: loggers, the snippet and user models, a template cache, a form decoder, and a session manager.
Sessions are stored in MySQL through scs, with a 12-hour lifetime. Passwords are hashed with bcrypt. The middleware chain handles panic recovery, request logging, secure headers, authentication, and CSRF protection.
The UI is server-rendered with html/template. Templates are parsed once at startup into a cache, and the static assets and templates are embedded into the binary with embed.FS, so deployment is a single executable plus a database.
There are tests around the handlers, template rendering, and middleware. They are not exhaustive, but they caught several regressions while I was changing the authentication flow.
What was hard
- Understanding the order of the middleware chain and which handlers should require authentication.
- Getting sessions and CSRF protection to work together without breaking form submissions.
- Learning how
html/templatehandles nested templates and dynamic data. - Making database errors meaningful instead of leaking them into the HTTP response.
The biggest shift was learning to think in terms of a request passing through a chain of small wrappers, each with one job.
What I’d do next
- Add integration tests against a real MySQL instance and a Docker Compose setup for local development.
- Add pagination and search for snippets.
- Add rate limiting on the login and signup endpoints.
- Deploy it behind Nginx and write a short operational runbook.
- Replace the text logs with structured logging and request IDs.
This project is where Go’s standard library stopped feeling like a list of packages and started feeling like a set of tools I could combine.