# The course this project was built from This project was built incrementally across 10 lessons, each adding one concept on top of the last. This file is a map from "concept" to "where it lives in the code" - useful if you want to revisit how/why something was built the way it was. | # | Lesson | New concepts | Where it lives | |---|---|---|---| | 1 | Project skeleton & chi routing | Standard Go project layout, `chi.Mux`, middleware basics, graceful shutdown via `http.Server` + `srv.Shutdown()` | `cmd/api/main.go`, `internal/router/router.go`, `internal/handlers/health.go` | | 2 | Structured JSON logging | `log/slog`, `slog.NewJSONHandler`, log levels, the three-layer middleware-factory pattern | `internal/logging/logger.go`, `internal/middleware/request_logger.go` | | 3 | Config & MySQL connection | `database/sql`, connection pooling (`SetMaxOpenConns` etc.), DSNs, `context.WithTimeout` for a hard deadline on the initial ping | `internal/config/config.go`, `internal/database/mysql.go` | | 4 | User model & repository pattern | Pointers (`*`/`&`) in depth, pointer receivers, the repository pattern, sentinel errors + `errors.Is` | `internal/models/user.go`, `internal/models/user_repository.go` | | 5 | Password login | `bcrypt` hashing/salting, decoding JSON request bodies, struct tags, generic error messages to avoid user enumeration | `internal/handlers/auth.go` (Register/Login), `internal/handlers/respond.go` | | 6 | Server-side sessions (scs + Redis) | `scs.SessionManager`, swapping storage backends via `.Store`, cookie flags (`HttpOnly`, `SameSite`), `RenewToken` to prevent session fixation | `internal/session/session.go`, `internal/session/keys.go`, `Login`/`Logout`/`Me` in `internal/handlers/auth.go` | | 7 | Login with Google (OAuth2) | Authorization Code flow, `oauth2.Config`, CSRF `state` parameter, account linking by email | `internal/oauth/google.go`, `internal/handlers/oauth_google.go` | | 8 | Auth middleware & route protection | `context.Context` (`WithValue`/`Value`), private context-key types, type assertions, chi route groups | `internal/middleware/require_auth.go`, `r.Group(...)` in `internal/router/router.go` | | 9 | Rate limiting & security hardening | `httprate.LimitByIP`, CORS (`go-chi/cors`), environment-aware `Secure` cookie flag | `internal/router/router.go`, `internal/session/session.go`, `internal/config/config.go` | | 10 | Docker & wrap-up | Multi-stage Docker builds, `docker-compose`, service-name-as-hostname networking, named volumes | `Dockerfile`, `docker-compose.yml` | ## Core Go ideas that recur throughout the codebase These aren't tied to a single lesson - once introduced, they show up repeatedly, and are worth having solid: - **Pointers (`*` / `&`)** - sharing one instance of something stateful (`*sql.DB`, `*scs.SessionManager`, `*slog.Logger`) across the whole app instead of copying it; writing a result back into a caller's variable (`rows.Scan(&x)`, `u.ID = int(id)` inside `Create(ctx, u *User)`). - **Interfaces satisfied implicitly** - `*chi.Mux` satisfies `http.Handler` just by having a `ServeHTTP` method; there's no `implements` keyword in Go. - **Closures / the three-layer middleware pattern** - seen in both `RequestLogger(logger)` and `RequireAuth(sessions, userRepo, logger)`: an outer function captures dependencies, returns a `func(http.Handler) http.Handler`, which itself returns the actual per-request handler - three layers, each running at a different time. - **`context.Context`** - carrying request-scoped values (the current user, a request ID) and deadlines (timeouts) through a call chain without adding extra parameters to every function signature. - **Error wrapping and sentinel errors** - `fmt.Errorf("...: %w", err)` to add context while preserving the original error; `var ErrUserNotFound = errors.New(...)` plus `errors.Is(err, ErrUserNotFound)` to let callers branch on error *kind* without string-matching messages. - **Dependency injection via structs** - `AuthHandler{userRepo, sessions, logger}` instead of global variables, so every handler's requirements are explicit and visible in its constructor. ## Suggested next steps If you want to keep extending this project as further practice: 1. **Testing** - `httptest.NewRequest`/`NewRecorder` for handler tests, table-driven test cases, and extracting a `UserStore` interface so `UserRepository` can be swapped for an in-memory fake in tests. 2. **A real migration tool** (e.g. `golang-migrate/migrate`) instead of `CREATE TABLE IF NOT EXISTS` on every boot. 3. **CSRF tokens** if you ever add a same-origin HTML form frontend (the current `SameSite=Lax` cookie already covers the JSON-API case). 4. **Refresh/renewal** so an active user's session doesn't hard-expire after 24 hours regardless of activity. 5. **Machine-readable error codes** (`{"error_code": "invalid_credentials"}`) so a frontend can branch on a stable code instead of parsing message text. 6. **Grafana Alloy + Loki** - point Alloy at this container's stdout; the JSON shape from `internal/logging` and `internal/middleware/request_logger.go` is already structured for it.