Files
web/server/README.md
2025-12-18 12:36:29 -05:00

116 lines
3.7 KiB
Markdown

# go-server
A minimal, production-ready, reusable HTTP server foundation for Go web services using **Chi** router and **slog** structured logging.
This package provides everything you need to spin up a secure, observable, and gracefully shutdown-capable web server with almost zero boilerplate.
## Features
- Automatic TLS support (via env vars)
- Graceful shutdown on `SIGINT` and `SIGTERM`
- Structured JSON logging with `log/slog`
- Request ID generation and propagation
- Panic recovery with stack traces
- Structured access logging (method, path, duration, status, bytes, request_id)
- Built-in `/healthz` and `/readyz` endpoints
- Configurable timeouts (read, write, idle, shutdown)
- Functional options for clean configuration
- Full request-scoped contextual logging via `slog.Logger.With()`
## Installation
```bash
go get git.citc.tech/go/web/server
```
## Quick Start
```go
package main
import (
"os"
"github.com/go-chi/chi/v5"
"git.citc.tech/go/web/server"
)
func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from your new service!"))
})
// Optional: custom logger, timeouts, etc.
srv := server.New(
server.WithRouter(r),
// server.WithLogger(customLogger),
// server.WithShutdownTimeout(20 * time.Second),
)
if err := srv.Start(); err != nil {
srv.Log.Error("server stopped with error", "error", err)
os.Exit(1)
}
}
```
That's it — your service is now running with full production features.
## Configuration (Environment Variables)
| Variable | Description | Default |
|------------------------------|--------------------------------------|--------------|
| `APP_SERVER_ADDR` | Listen address (host:port) | `:8080` |
| `APP_SERVER_TLS_KEY_FILE` | Path to TLS private key | (none) |
| `APP_SERVER_TLS_CERT_FILE` | Path to TLS certificate | (none) |
TLS is automatically enabled if both key and cert files exist and are readable.
## Logging
- Uses `log/slog` with JSON output by default
- All logs include timestamps and levels
- Access logs include duration, status, bytes, and request_id
- Panics are recovered and logged with full stack trace
- Request-scoped logging: use `server.LoggerFromContext(r.Context())` in handlers for logs that automatically include `request_id`, `method`, `path`, etc.
### Example Handler with Request-Scoped Logging
```go
func protectedHandler(w http.ResponseWriter, r *http.Request) {
log := server.LoggerFromContext(r.Context())
log.Info("handling protected request", "action", "load_dashboard")
// All logs here automatically include request_id, method, path, etc.
log.Debug("fetching user data", "user_id", 123)
w.Write([]byte("Protected content"))
}
```
## Middleware Stack (Applied by Default)
1. Panic recovery (with structured error logging)
2. Request ID generation (`X-Request-ID` header)
3. Structured request logging
You can override the router completely with `WithRouter()` — middleware will still apply unless you replace the router after `New()`.
## Options
```go
server.WithLogger(logger *slog.Logger)
server.WithRouter(router chi.Router)
server.WithReadTimeout(duration time.Duration)
server.WithWriteTimeout(duration time.Duration)
server.WithIdleTimeout(duration time.Duration)
server.WithShutdownTimeout(duration time.Duration)
```
## Health Endpoints
- `GET /healthz` → returns "ok" (200)
- `GET /readyz` → returns "ready" (200)