A minimal REST API skeleton built with Huma running on top of Chi via humachi. It demonstrates structured logging, RFC 9457 Problem Details for errors, and a modular route layout that is ready to grow into a larger service.
Gopher illustration from free-gophers-pack by Maria Letta
- Layered middleware architecture with security headers, CORS, request IDs, real IP detection, and structured access logs
- Request-scoped Zap logger with Google Cloud Trace correlation via W3C Trace Context
traceparentheader, falling back to request ID when no trace exists - RFC 9457 Problem Details for all error responses with optional field-level validation errors
- Content negotiation supporting JSON (RFC 8259) and CBOR (RFC 8949) formats via
Acceptheader - Cursor-based pagination with RFC 8288 Link headers
- OpenAPI 3.1 documentation with Swagger UI, auto-generated from Huma route schemas
- Health check endpoint (
/health) for liveness probes
- Use plural nouns for collections (
/users, not/user) - Avoid verbs in URIs; let HTTP methods convey the action
- Nest resources to express relationships (
/posts/{postId}/comments); limit nesting to one level
| Method | Purpose | Success Status |
|---|---|---|
| GET | Retrieve resource(s) | 200 OK |
| POST | Create a resource | 201 Created |
| PUT | Replace a resource entirely | 200 OK or 204 No Content |
| PATCH | Partial update | 200 OK or 204 No Content |
| DELETE | Remove a resource | 204 No Content |
Errors follow RFC 9457 Problem Details and honor content negotiation:
application/problem+jsonwhen JSON is requested (default)application/problem+cborwhen CBOR is requested
| Status | Use Case |
|---|---|
| 400 Bad Request | Malformed syntax, missing required fields |
| 422 Unprocessable Entity | Validation failures on specific fields |
- Default:
application/json(RFC 8259) - Alternate:
application/cbor(RFC 8949) - Format selected via
Acceptheader with q-value support
- Cursor-based tokens for stability
- Links provided via HTTP
Linkheader per RFC 8288
- Go 1.25+
- Just command runner (optional)
This project uses a Go workspace (go.work) to manage multiple modules:
go 1.25.5
use (
.
./functions
)The workspace allows simultaneous development across modules. Commands like go build, go test, and go mod tidy operate on all workspace modules when run from the root.
go run ./cmd/serverThen visit:
http://localhost:8080/health- service health probehttp://localhost:8080/v1/api-docs- interactive API explorerhttp://localhost:8080/v1/openapi- generated OpenAPI schema
Sample request:
curl -s localhost:8080/health | jqCopy .env.example to .env and customize as needed:
cp .env.example .env| Variable | Description | Default |
|---|---|---|
PORT |
Server listen port | 8080 |
HOST |
Host address to bind to | 0.0.0.0 |
LOG_LEVEL |
Log level (debug, info, warn, error) | info |
FIREBASE_PROJECT_ID |
Firebase project ID for Cloud Trace correlation | - |
APP_ENVIRONMENT |
Environment label | development |
APP_URL |
Base URL for the application | http://localhost:8080 |
cmd/server/ # Application entrypoint and HTTP server bootstrap
internal/http/ # HTTP transport layer
health/ # Health check handler (unversioned)
v1/ # Versioned API (v1)
hello/ # Hello endpoint handlers
items/ # Items endpoint handlers
routes/ # Route registration
internal/platform/ # Cross-cutting infrastructure
logging/ # Structured logging with Zap
middleware/ # Security headers, CORS, request ID
pagination/ # Cursor-based pagination
respond/ # Panic recovery and Problem Details
timeutil/ # Time formatting utilities
functions/ # Cloud Functions (separate Go module)
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check route |
| GET | /v1/hello |
Default greeting |
| POST | /v1/hello |
Create a personalized greeting |
| GET | /v1/items |
List items with cursor-based pagination |
go build -v ./... # Build
go test ./... # Run tests
go test -v ./... # Verbose output
golangci-lint run ./... # Lint| Command | Description |
|---|---|
just build |
Build the application |
just run |
Run the server |
just test |
Run all tests |
just lint |
Run linter |
just check |
Full check (build + test + lint) |
Run just to see all available commands.
go mod download # Download dependencies
go get -u -t ./... # Update dependencies
go mod tidy # Clean up go.mod- Create a new package under
internal/http/v1/(e.g.,users/handler.go) - Define your handler using Huma with an output struct containing a
Bodyfield - Add a registration function and call it from
routes.Register - Return errors using Huma's error helpers (
huma.Error400BadRequest(), etc.)
just container-build # Build image
just container-up # Run container detached
just container-down # Stop containerOr with Docker/Podman CLI:
docker build -t huma-playground:latest .
docker run --rm -p 8080:8080 --env-file .env huma-playground:latest# Build and push
gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPO/huma-playground:latest
# Deploy with automatic base image updates
gcloud run deploy huma-playground \
--image REGION-docker.pkg.dev/PROJECT_ID/REPO/huma-playground:latest \
--platform managed \
--region REGION \
--base-image go125 \
--automatic-updates
# Deploy from source with automatic base image updates
gcloud run deploy huma-playground \
--source . \
--platform managed \
--region REGION \
--base-image go125 \
--automatic-updatesThe --base-image and --automatic-updates flags enable automatic base image updates, allowing Google to apply security patches to the OS and runtime without rebuilding or redeploying.
Set a FIREBASE_PROJECT_ID environment variable to enable trace correlation in Cloud Logging.
GitHub Actions workflows in .github/workflows/:
| Workflow | Description |
|---|---|
app-ci.yml |
Build, tests, and coverage report |
app-lint.yml |
Code quality (golangci-lint) |
labeler.yml |
Automatic PR labeling |
labeler-manual.yml |
Manual labeling for historical PRs |
dependabot-auto-merge.yml |
Auto-merge Dependabot minor/patch updates |
Dependabot is configured in .github/dependabot.yml for automated dependency updates.
See AGENTS.md for coding guidelines and conventions.
MIT