Gin architect
Skill ralvarezdev/ralvaskills/skills/frameworks/gin-architect
My personal, ever-growing collection of AI skills for OpenCode and Claude Code. Enforces strict clean architecture and professional standards.
npx -y skills add ralvarezdev/ralvaskills --skill gin-architectAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Framework-specific delta on rest-api-architect — Gin 1.12 on Go 1.26. Feature layout, struct-tag validation, RFC 7807 errors, in-house JWT or external IdP, route groups for URL-prefix versioning, OpenAPI. Read rest-api-architect first for the cross-cutting REST conventions. Use when scaffolding or reviewing a Gin service.
SKILL.md
7.1 KB, as published. Nobody here has run it
Gin Architecture
Targets Gin 1.12 on Go 1.26. Companion to go-architect, rest-api-architect, and sql-architect. Implementation skeletons in RECIPES.md; pinned deps in STACK.md.
1. Project structure — feature-based
One folder per bounded context. Each feature owns its routes, service, repo, DTOs, and SQL files. Mirrors fastapi-architect so polyglot teams can navigate either side. Full tree in RECIPES.md.
handlers.godepends onservice.go; never reaches intorepo.godirectly.service.gois pure Go — noginimports. Easy to unit-test without a fake*gin.Context.dto.goholds wire types withjson:andbinding:(validator) tags. Never reuse domain structs as DTOs — that's how internal fields leak into the API.
2. Routing & versioning
URL-prefix versioning via route groups. One group per version, one sub-group per feature. Registration skeleton in RECIPES.md.
- One
Register(rg, deps)function per feature — keepsmain.gothin. - Path params typed at parse time:
id, err := uuid.Parse(c.Param("user_id"))— return 400 on parse failure. - Use
gin.RouterGroup, not bareEngine.GET, so versioning + per-group middleware stays clean.
3. Request validation
Use struct tags with go-playground/validator (canonical per go-architect). Bind via c.ShouldBindJSON / c.ShouldBindUri / c.ShouldBindQuery — never c.MustBindWith (panics; we don't panic in handlers).
type CreateUserReq struct {
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=12"`
}
binding:"required"on every field that isn't truly optional.- Custom validators registered once at startup:
validate.RegisterValidation("uuid_v7", isUUIDv7). binding:"omitempty"on PATCH partial-update DTOs — let absent fields mean "leave unchanged."
4. Response shaping
- Return DTOs, not domain types.
c.JSON(200, dto.UserResponse{...})— DTOs control what leaks. json:"-"on any DTO field that should never serialize (passwords, internal IDs).- Status code with
c.JSON(http.StatusCreated, ...)— explicit, not Gin's default 200. - Empty body uses
c.Status(http.StatusNoContent), notc.JSON(204, nil)(sendsnull).
5. Dependency injection
Explicit constructors per go-architect §3. No globals, no init() magic. Dependencies live in a Deps struct wired in main.go. For larger graphs use uber-go/fx (canonical per go-architect); for services with <10 dependencies, hand-wired is clearer.
6. Lifespan & graceful shutdown
Open shared resources in main.go, never per-request. Close them on shutdown signal via signal.NotifyContext (Go 1.26 records which signal fired). Full skeleton in RECIPES.md.
ReadHeaderTimeoutis mandatory — without it, Slowloris can pin connections forever.- Graceful shutdown timeout > longest expected request duration.
7. Authentication & authorization
Patterns (in-house JWT vs external IdP, Argon2id, JWT lifetimes, JWKS verification, switching criterion) live in rest-api-architect/AUTH_PATTERNS.md. Gin specifics:
- Pattern A — in-house JWT via
golang-jwt/jwt/v5+argon2fromgolang.org/x/crypto. Middleware skeleton in RECIPES.md. - Pattern B — external IdP:
jwt.ParseWithClaimswith aKeyfuncthat resolves keys via a JWKS client. Cache JWKS in-process with TTL. - Authorization per route, never global —
RequireScope(...)composed alongsideAuthRequired(secret)on the route declaration (see RECIPES.md).
8. Error handling — RFC 7807 middleware
A central problem package emits application/problem+json (per rest-api-architect §7). Handlers either call problem.Render(c, p) directly or c.Error(err) and let the recovery middleware convert. Renderer in RECIPES.md.
- One handler per domain-error family — map known sentinel errors to
Problemtypes in a single switch. gin.Recoverywith a customRecoveryHandlerthat emits a 500Problemwithcorrelation_id— never a stack trace.
9. Middleware order
Outermost first; order matters.
r := gin.New() // not gin.Default — we set logging ourselves
r.Use(
middleware.RequestID(), // 1. assign correlation_id
middleware.SLog(), // 2. structured access log
gin.Recovery(), // 3. convert panics → 500 problem
middleware.CORS(cfg.CORS), // 4. preflight handling
gzip.Gzip(gzip.DefaultCompression), // 5. response compression
)
- Never
gin.Default()in production — its logger writes unstructured text to stdout. Useslog(per go-architect). - Auth is per-route middleware, never global (see §7).
- Recovery before CORS — a panic that bypasses CORS handler returns no headers; the browser shows a misleading CORS error.
10. Concurrency, not "background tasks"
Go has no FastAPI-style BackgroundTasks. To run work after the response, derive a new context.Context from context.Background() (request context is cancelled the moment the response writes), set a timeout, log errors. Anything serious (retryable, distributed, scheduled) belongs in a real task queue, not a goroutine.
11. Testing
httptest.NewRecorder + the engine directly — no real socket. Skeleton in RECIPES.md.
- Test-DB strategy: mirror
sql-architect— wrap each test in a rolled-back transaction, or use a per-test schema withgolang-migrate. - Table-driven tests (per go-architect §9) for request validation paths — one row per (input, expected status, expected error type).
12. OpenAPI generation
- Default:
swaggo/swag—// @annotation comments above handlers;swag initgeneratesdocs/swagger.jsonandswagger.yaml. Predictable, mature. - For full OpenAPI 3.1 control:
getkin/kin-openapi— write the spec in code; serve it; use it to validate requests at runtime. More work but no annotation noise. - CI snapshot-tests the spec (per rest-api-architect §15).
- Internal endpoints excluded with
// @Hidden(swag) or by not registering them in the spec route group.