A production-ready Go HTTP server template demonstrating modern Go patterns and best practices.
Core Functionality:
- Fully unit and unit-integration testable JSON HTTP web server
- Modern handler pattern with dependency injection (following Grafana's approach)
- Graceful shutdown with proper resource cleanup
- Context propagation throughout request handling
- Health check endpoint with database connectivity verification
Observability:
- Structured logging with
slog - Key-value error data via
kverrpackage for better structured error logging - Prometheus metrics for HTTP requests and database connection pools
- Metrics labeled by data store (taskqueue, events) for better observability
- Grafana and Loki integration for log aggregation
Testing:
- Able to spin up multiple running HTTP servers in parallel during tests
- Able to assert against the server's logged contents
- Fakes as test doubles, demonstrating practical alternatives to mock frameworks
- Comprehensive middleware tests
- Table-driven tests for maintainability
Architecture:
- Internal packages (
/internal/) for implementation details - Each component responsible for its own observability
- Dependency injection at route definition time
- No global state (except where explicitly documented)
Handler Pattern:
- Handlers are standalone functions that receive dependencies via closure
- Dependencies are injected at route definition:
router.Get("/", handleHelloworld(eventStore)) - Logger is injected via middleware and accessed through request context
- No handlers are methods on the Server struct
Package Structure:
/internal/taskqueue/- Task queue implementation (internal)/internal/events/- Event store implementation (internal)/server/- HTTP server and handlers/logger/- Structured logging utilities/metrics/- Prometheus metrics definitions
Design Choices:
- Test servers take Options for flexible configuration (log buffers, custom configs, etc.)
- Logger is placed in request context via middleware
- Each data store (taskqueue, events) manages its own database connection metrics
- Metrics use
storelabel to distinguish between different data stores - Graceful shutdown ensures all components (HTTP servers, event store, task queue) are properly closed
Install make, docker, docker compose, and go (1.23+).
Recommended: alias dc='docker compose'
# Terminal 1: Start database
dc up mysql
# or
docker compose up -d mysql
# Terminal 2: Start server
source settings.env
go run cmd/helloworld/main.go
# Terminal 3: Test the server
curl localhost:16666/
# {"hello":"World!","event_store_available":true,"event_store_message":"Event store is available"}
curl localhost:16667/healthcheck
# 200 OK (or 503 if database is unreachable)Configuration is loaded from environment variables with the HELLOWORLD_ prefix. See settings_example.env for available options.
Key Configuration Options:
HELLOWORLD_DB_HOST- Database host (default:127.0.0.1)HELLOWORLD_DB_USER- Database user (default:testuser)HELLOWORLD_DB_PASS- Database password (default:testuser)HELLOWORLD_DB_NAME- Database name (default:helloworld)HELLOWORLD_PORT- Public HTTP port (default:16666)HELLOWORLD_INTERNAL_PORT- Internal metrics/health port (default:16667)HELLOWORLD_REQUEST_TIMEOUT- Request timeout duration (default:30s)HELLOWORLD_ENABLE_DEBUG- Enable debug logging (default:true)HELLOWORLD_SHOULD_SECURE- Enable secure cookies and TLS (default:false)
Security Notes:
- When
HELLOWORLD_SHOULD_SECURE=true,HELLOWORLD_DB_CA_CERT_PATHmust be set - Passwords are never logged (DSN is masked in debug logs)
- CORS uses explicit origins even in development mode
Metrics:
- Prometheus metrics available at
http://localhost:16667/metrics - HTTP metrics: request count, duration, in-flight requests
- Database connection pool metrics labeled by store (
taskqueue,events)
Logs:
- Structured JSON logging via
slog - Request-scoped loggers with request IDs
- Errors include structured key-value pairs via
kverr
Grafana Dashboard:
dc up -d mysql grafana
go run cmd/helloworld/main.go >> ./logs/helloworld.logs
# Open http://localhost:3000Metrics are polled from the internal port /metrics to Prometheus where Grafana displays data. Similarly, Promtail follows logs and pushes them into Loki, where Grafana can display the results.
Note: This requires Prometheus and Promtail to be able to reach the host system outside the docker container.
Unit Tests:
go test ./...
# or
make testTest Types:
- Unit tests - Fast, isolated tests with fakes/mocks
- Middleware tests - Test CORS, timeout, and other middleware
- Integration tests - Test against real database
- Unit-integration tests - Headless browser tests from unit test framework
Integration Testing:
# May need to prefix with "sudo" if you get permission errors
make test-integration # Tear down DB, reseed, rebuild, and test blackbox style
make test-integration-docker # Run everything in docker, like CI/CDTest Features:
- Dynamic port allocation for parallel test execution
- Log buffer assertions for verifying logged content
- Graceful shutdown testing
- Context timeout and propagation testing
GET /- Hello world endpoint- Query params:
?delay=<duration>- Simulate delayed response (e.g.,?delay=1s) - Response:
{"hello":"World!","event_store_available":bool,"event_store_message":string}
- Query params:
GET /healthcheck- Health check endpoint- Returns
200 OKif database is reachable - Returns
503 Service Unavailableif database is unreachable
- Returns
GET /metrics- Prometheus metrics endpoint
# Build binary with version from VERSION file
make build
# Binary will be in ./bin/helloworld# Build and run with docker compose
docker compose build helloworld
docker compose up helloworld- Set
HELLOWORLD_SHOULD_SECURE=truefor production - Configure
HELLOWORLD_DB_CA_CERT_PATHfor secure database connections - Use explicit CORS origins (not wildcards)
- Set appropriate timeouts (
HELLOWORLD_REQUEST_TIMEOUT,HELLOWORLD_SHUTDOWN_TIMEOUT) - Monitor metrics at
/metricsendpoint - Ensure graceful shutdown is respected by orchestration (Kubernetes, etc.)
- Handlers receive dependencies via closure, not as methods on Server
- Logger is injected via middleware, accessed through request context
- Errors are wrapped with context using
fmt.Errorf("...: %w", err) - Use
kverrfor structured error data - Each component manages its own observability (metrics, logging)
.
├── cmd/
│ └── helloworld/ # Main application entry point
├── internal/
│ ├── events/ # Event store implementation
│ ├── taskqueue/ # Task queue implementation
│ └── util/ # Internal utilities
├── server/ # HTTP server and handlers
├── logger/ # Logging utilities
├── metrics/ # Prometheus metrics
└── migrations/ # Database migrations
- Make a testcase showing grpc handling
- Make a testcase showing graphql handling
- UI route handling? or just keep this as a JSON api server?