From go-backend-skills
Go API patterns for chi router, pgkit data layer, and webrpc services. Use when writing or reviewing Go backend code including HTTP handlers, database table methods, transactions, error handling, or integration tests.
How this skill is triggered — by the user, by Claude, or both
Slash command
/go-backend-skills:go-chi-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
For modern Go idioms (atomics, goroutines, slices/maps, error handling), see
For modern Go idioms (atomics, goroutines, slices/maps, error handling), see
the modern-go skill. For external service clients, see clients. For
database migrations, see postgresql.
| Rule | Do | Don't |
|---|---|---|
| Interface pointers | func(w io.Writer) | func(w *io.Writer) |
| Receivers | Consistent pointer (s *Service) | Mix value/pointer on same type |
| Zero-value mutex | var mu sync.Mutex | mu := new(sync.Mutex) |
| Slice/map boundaries | Copy at API boundaries | Return internal slices/maps |
| Defer | Outside loops; wrap body in closure inside loops | defer inside loops |
| Channel size | 0 or 1 | Arbitrary buffer sizes |
| Time | time.Time, time.Duration | int64 for timestamps |
| Error types | var ErrFoo = errors.New() | String comparison |
| Type assertion | v, ok := x.(T) | v := x.(T) panics |
| Panic | Only in main() or must.* init helpers | Panic in services/clients |
| Global state | Dependency injection | Mutable globals |
| Embedding | Private structs only | Embed in public structs |
| Rule | Do | Don't |
|---|---|---|
| Int conversion | strconv.Itoa(n) | fmt.Sprintf("%d", n) |
| String/byte | Reuse []byte, minimize conversion | Repeated []byte(s) in loops |
| Map/slice capacity | make(map[K]V, hint), make([]T, 0, cap) | Grow incrementally |
| Rule | Do | Don't |
|---|---|---|
| Line length | ~99 chars | Overly long lines |
| Imports | stdlib, external, internal (grouped) | Mixed groups |
| Package names | Short, lowercase, singular | _, util, common |
| Nesting | Early return, max 2 levels | >3 levels |
| Control flow | Separate if guards; no else/else if | if/else if chains (use switch or separate ifs) |
| Variable scope | Declare near use | All vars at function top |
| Struct params | Use struct for 3+ params | Long parameter lists |
| Struct init | Field names, &S{...}, omit zero fields | Positional literals |
| Getters | Name() | GetName() |
| Rule | Do | Don't |
|---|---|---|
| Handler registration | chi route groups with scoped middleware | Register on root router directly |
| Shared deps | Embed a core struct (DB, Config, Log) in every service | Pass individual deps |
| Service layout | service.go + one file per endpoint | All endpoints in one file |
| Route grouping | r.Group(func(r chi.Router) {...}) | Broad middleware on narrow endpoints |
| Rule | Do | Don't |
|---|---|---|
| Public errors | Typed RPC/HTTP errors | errors.New("...") for user-facing |
| Wrapping cause | Typed error with cause | fmt.Errorf("...: %w", typedErr) |
| Handle once | Check immediately after call | Log and return same error |
| Required dependency | Fail the request if a dependent op fails | Log-and-continue when op is required for correctness |
When a handler touches both the local DB and an external service:
| Rule | Do | Don't |
|---|---|---|
| Ordering | External side effect first, DB commit after | DB commit first then hope external call succeeds |
| Failure | If external call fails, return error | Delete DB row then call external API |
| Blast radius | Match external API scope to intent | Assume external API scope matches your intent |
| Config deps | Validate dependent config fields together at construction | Check one field, ignore its required companion |
| Rule | Do | Don't |
|---|---|---|
| All queries | Named methods on table types in data layer | Raw SQL in service handlers |
| DB access | s.DB.Xxx.Method(ctx, ...) via injected DB struct | Global data.DB |
| Transactions | Begin + defer rollback + named err return | Forget rollback; unnamed return |
| Tx scope | Pass tx-scoped DB to table methods | Table methods without tx context |
| Error sentinel | data.IsErrorNoRows(err) or equivalent | String matching |
| Pagination | Fetch N+1, trim to N | COUNT(*) queries |
Transaction pattern:
func (s *Service) Op(ctx context.Context) (result *T, err error) {
tx, err := s.DB.Conn.Begin(ctx)
if err != nil { return nil, fmt.Errorf("begin tx: %w", err) }
defer func() {
if rbErr := tx.Rollback(ctx); rbErr != nil && !errors.Is(rbErr, pgx.ErrTxClosed) {
err = errors.Join(err, rbErr)
}
}()
db := s.DB.Tx(tx)
// ... use db.Table.Method(ctx, ...) ...
if err := tx.Commit(ctx); err != nil { return nil, fmt.Errorf("commit tx: %w", err) }
return result, nil
}
| Rule | Do | Don't |
|---|---|---|
| Section headers | kebab-case [api-keys] | snake_case [api_keys] |
| Field keys | snake_case public_key | kebab-case public-key |
| Secrets | Environment variables or secret manager | Hardcoded secrets in config |
| Config reads | Centralized config loader | Env reads scattered in code |
| Rule | Do | Don't |
|---|---|---|
| Logger | *slog.Logger injected | log.Printf, fmt.Println |
| Fields | slog.String("op", "name") | String concatenation |
| Context | Use context-aware logger for trace ID | Bare logger in handlers |
| Sensitive | Never log tokens/passwords/PII | Log creds "for debugging" |
| Rule | Do | Don't |
|---|---|---|
| Test type | E2E integration via real HTTP + real DB | Mock DB, direct service calls |
| Assertions | require when nil would panic; assert otherwise | if err != nil { t.Fatal } |
| Error checks | err := fn() then require.NoError(t, err) | require.NoError(t, fn()) inline |
| Context | t.Context() | context.Background() |
| DB state | Truncate all tables per test | Assume prior state |
Never: mutable globals, os.Exit outside main(), panic outside cmd/,
bypassing service layer for DB access, lib/ importing rpc/.
npx claudepluginhub klaidliadon/skillsSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.