Updated Jul 17, 2026

16 - Put It All Together

📋 Jump to Takeaways

🎁 You've collected fifteen lessons' worth of pieces. What does a real, deployable Go service look like when all of them snap together into one project?

You've learned project structure, configuration, logging, routing, middleware, error handling, databases, templates, embedding, graceful shutdown, profiling, and deployment. Now let's see the complete bookmarks project, every file, wired together, ready to run.

This lesson has no new concepts. It's the finished product. Use it as a reference when something doesn't work, or clone it to start fresh.

Project Overview

A bookmarks app with two interfaces:

  • JSON API: GET, POST, DELETE on /api/bookmarks for programmatic access
  • HTML UI: a server-rendered page at / where you can add and delete bookmarks in the browser

Both share the same data layer. One store, two presentations.

Directory Structure

bookmarks/
├── main.go              # Entry point: config, routes, graceful shutdown
├── config.go            # Environment variables + flag overrides
├── model.go             # Bookmark struct
├── store.go             # Database layer (database/sql + lib/pq)
├── handler.go           # JSON API handlers
├── handler_html.go      # HTML handlers (list, create, delete)
├── middleware.go         # Logger, Recovery, RequestID, CORS, Chain
├── templates/
│   ├── layout.html      # Base HTML layout
│   └── list.html        # Bookmarks page with form
├── static/
│   └── style.css        # Minimal styles
├── go.mod
├── go.sum
├── .env                 # Local environment variables
├── Makefile             # Build, run, test, lint commands
├── Dockerfile           # Multi-stage build
├── docker-compose.yml   # Postgres + API
└── .dockerignore

Lesson-by-Lesson Recap

Every lesson added one piece. This is the assembled result:

File Lesson(s) What it does
main.go Project Structure, Graceful Shutdown Loads config, opens the database, creates the store, parses templates, registers routes, wraps the mux in Chain, and starts the server with graceful shutdown
config.go Configuration Reads PORT, DATABASE_URL, and LOG_LEVEL from environment variables with sensible defaults. Flags can override port and log level
model.go Project Structure Defines the Bookmark struct, shared by the store, API handlers, and templates
store.go Database Opens a *sql.DB with pool settings, auto-creates the table on startup, and provides Create, GetByID, List, and Delete. All methods take context.Context
handler.go HTTP Routing, Error Handling JSON API: writeJSON/writeError helpers plus list, create, get, and delete handlers. Each is a closure that captures the store
handler_html.go Templates HTML handlers: list renders the template, create reads r.FormValue and redirects (Post/Redirect/Get), delete takes the ID from the path and redirects
middleware.go Middleware, Structured Logging Provides RequestID, Recovery, CORS, Logger, and the Chain function that wraps them in order
templates/ Templates Go's html/template with a layout + page pattern. The list page has an add form and per-item delete buttons
Makefile, Dockerfile Deployment Build, run, test, and lint commands plus a multi-stage container image

Running the Final Project

docker compose up

This starts Postgres and the API. Open http://localhost:8080.

Without Docker

Start Postgres locally, then:

# Set environment variables
export DATABASE_URL="postgres://user:pass@localhost:5432/bookmarks?sslmode=disable"
export PORT=8080

# Build and run
make run

Or use the .env file:

make run   # Makefile loads .env automatically

Test the API

# Create a bookmark
curl -X POST http://localhost:8080/api/bookmarks \
  -H "Content-Type: application/json" \
  -d '{"url": "https://go.dev", "title": "Go"}'

# List all bookmarks
curl http://localhost:8080/api/bookmarks

# Delete a bookmark
curl -X DELETE http://localhost:8080/api/bookmarks/1

Or just open http://localhost:8080 in your browser and use the form.

Key Takeaways

  • A production Go service is a handful of files, each with a clear responsibility
  • The standard library handles routing, middleware, templates, database access, and graceful shutdown, no frameworks needed
  • Separate your JSON API from your HTML handlers. They share the store but serve different clients
  • Use docker compose to run the full stack locally with one command
  • The complete source files are in the examples below. Copy them to start your own project

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

Spot something off? Report an issue
© 2026 ByteLearn.dev. Free courses for developers. · Privacy