Go API Design Review Skill
Handler, error, and context conventions for automated review - a cookbook-style Agent Skill for auditing Go 1.26 HTTP and gRPC handlers without unsafe auto-fixes.
Search across all documentation pages
Handler, error, and context conventions for automated review - a cookbook-style Agent Skill for auditing Go 1.26 HTTP and gRPC handlers without unsafe auto-fixes.
Produces a structured review checklist for handlers: context propagation, error mapping, status codes, request validation, middleware ordering, and response shape - each finding tied to file and line references.
| Input | Why |
|---|---|
go.mod | Module path, Go version directive |
| Affected packages | Scope go test and lint |
| Router choice | stdlib ServeMux vs chi/gin/echo per ADR |
| Error-handling ADR | Typed errors vs sentinel pattern |
| Auth/logging ADR | Middleware expectations |
| OpenAPI or route table | Expected methods and paths |
file:line referencesgo test ./pkg/..., golangci-lint run ./pkg/...r.Context() to all outbound I/O - DB, HTTP client, gRPC.apierr package, not both.errcheck, contextcheck) without ADR exception.Quick-reference recipe card - copy-paste ready.
// Review targets (stdlib handler)
func getUser(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // must flow to store.GetUser(ctx, id)
id := r.PathValue("id")
if id == "" {
writeError(w, apierr.BadRequest("missing id"))
return
}
u, err := store.GetUser(ctx, id)
if err != nil {
writeError(w, mapStoreErr(err))
return
}
writeJSON(w, http.StatusOK, u)
}# Verification after review fixes
go test ./internal/api/...
golangci-lint run ./internal/api/...
go vet ./internal/api/...When to reach for this skill:
go list ./internal/api/...
grep -r "ServeHTTP\|HandleFunc\|chi\|gin\." internal/api/| Signal | Action |
|---|---|
Missing r.Context() on db.Query | Blocker |
http.Error(w, err.Error(), 500) on validation | Blocker |
Handler calls log.Printf and returns err text | Blocker |
Mixed fmt.Errorf and sentinel in same package | Warning |
Check every outbound call receives ctx derived from r.Context():
// BAD: context.Background() in handler
user, err := repo.Find(context.Background(), id)
// GOOD: request-scoped context
user, err := repo.Find(r.Context(), id)ReadHeaderTimeout, WriteTimeout) cancel r.Context() - downstream must respect itfunc mapStoreErr(err error) apierr.Response {
if errors.Is(err, store.ErrNotFound) {
return apierr.NotFound("user")
}
if errors.Is(err, store.ErrInvalid) {
return apierr.BadRequest("invalid user id")
}
return apierr.Internal("user lookup failed") // opaque to client
}codes.InvalidArgument, codes.NotFound - see grpc-Go patterns in sibling sections| Condition | Status | Body |
|---|---|---|
| Validation failure | 400 or 422 | { "error": "...", "code": "..." } |
| Auth failure | 401 / 403 | No internal details |
| Success | 200 / 201 | Resource or list per OpenAPI |
| Unknown server fault | 500 | Opaque ID + server-side log |
go test ./internal/api/... -count=1
golangci-lint run ./internal/api/...Middleware ordering affects review findings.
Logging and request ID middleware should wrap auth, which wraps business handlers.
Panic recovery belongs outermost.
Agents often insert auth after handlers in drafts - flag as blocker.
Validation belongs at the boundary: path params, query, JSON body.
Use encoding/json with strict decoding (DisallowUnknownFields) when ADR requires.
For chi/gin, confirm param extraction matches registered route patterns.
Idempotency and methods: GET and HEAD must not mutate state.
POST create vs PUT upsert policy should match OpenAPI.
Go 1.22+ ServeMux method patterns (GET /users/{id}) prevent accidental method-wide handlers.
http.Error with err.Error() leaks implementation details and often uses wrong status.context.WithTimeout without defer cancel() - linters catch some; review manually.WriteHeader - status locked on first write; use helper that sets headers once.http.Client has no timeout - flag outbound calls from handlers using http.DefaultClient.| Approach | When |
|---|---|
| Human design review only | Small teams, low traffic |
| OPA / policy-as-code | Org-wide HTTP standards |
| OpenAPI linter in CI | Contract-first APIs |
| This skill | Assisted PR review with team ADR alignment |
No. Output checklist and suggested diffs only. Human merges after go test and lint pass.
Yes for context, status mapping (codes.*), and error wrapping. HTTP-specific sections skip gRPC-only packages - collect router/proto inputs first.
Collect framework in inputs. Review gin.HandlerFunc for c.Request.Context() propagation and centralized c.JSON error helper.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026