Copy-paste Claude Code prompts for Gin handlers, middleware, sqlc queries, and table-driven tests. Go 1.22 idioms with context-first error handling.
Go's lack of magic makes Claude Code particularly useful — there's less invisible behaviour for it to forget. The prompts below produce idiomatic Gin handlers with proper context handling, structured errors, and table-driven tests.
Add POST /api/v1/projects to the gin router.
Constraints:
- Read internal/handlers/users/create.go first — match its shape exactly.
- Request type: ProjectCreateRequest in handlers/projects/types.go.
Validation tags: binding:"required,min=1,max=100" etc.
- Response type: ProjectResponse, same file.
- SQL query: add CreateProject in db/queries/projects.sql, sqlc generate
expected externally — write the SQL only.
- Handler signature: func(c *gin.Context). Use c.ShouldBindJSON, return
c.JSON(http.StatusCreated, resp) on success.
- Errors:
- validation → c.JSON(400, ErrorResponse{Code:"validation_error", Details: err.Error()})
- db constraint violation (use errors.As + *pq.Error or pgconn) → 409
- everything else → c.AbortWithStatusJSON(500, ErrorResponse{Code:"internal"})
- Log unexpected errors via log/slog with request_id from middleware.
- Register route in router/router.go in the v1 group with the existing auth middleware.
Write a Gin middleware RateLimiter that:
- Limits per (userID, route) to 60 req/min using a token-bucket from
golang.org/x/time/rate
- Stores limiters in a sync.Map keyed by "userID:routePath"
- On limit: c.AbortWithStatusJSON(429, ErrorResponse{Code:"rate_limited",
RetryAfterSeconds: int(reservation.Delay().Seconds())})
- Reads userID from context (set by auth middleware as "user_id" — read auth.go first)
- Skips for routes tagged with c.GetBool("skip_rate_limit")
Tests:
- Table-driven test in middleware/rate_limiter_test.go
- httptest.NewRecorder + httptest.NewRequest
- Sub-tests: under_limit_passes, over_limit_returns_429, separate_users_independent,
skip_flag_bypasses
- Each test sets a fresh limiter; do NOT share state across tests.
The /api/v1/exports endpoint launches a background goroutine and the pprof
goroutine count grows without bound. Read internal/handlers/exports/.
For each goroutine spawned in this package:
- file:line of the 'go ...' statement
- exit condition (what makes the goroutine return)
- whether it's bounded by a context cancel
- whether the caller waits or fire-and-forgets
Identify which one leaks and why. Propose the smallest fix:
- Add context with timeout, OR
- Add a sync.WaitGroup the caller waits on, OR
- Move to a worker pool
Do NOT introduce new dependencies. ants/errgroup are allowed if already in go.mod
(check first).
## Stack
Go 1.22, Gin 1.10, sqlc 1.27, Postgres 16, log/slog for logging,
testify only for assert/require — never for mocks.
## Conventions
- Package-by-feature under internal/.
- Handlers: one file per HTTP verb in handlers//.go.
- All handler errors return structured ErrorResponse — never c.String().
- Always pass ctx := c.Request.Context() into service/db calls.
- Tests: table-driven, sub-tests named in lower_snake_case.
Related: Claude API from Go.