go-chi-router
Originally fromcristiano-pacheco/ai-tools
SKILL.md
Go Chi Router
Generate Chi router implementations for Go backend HTTP transport layer.
When to Use
- Register HTTP routes for a module
- CRUD route setup (GET, POST, PUT, DELETE)
- Custom action endpoints (e.g., /activate, /deactivate)
- Route groups with middleware
- Versioned API routes
Location: internal/modules/<module>/http/chi/router/<resource>_router.go
Router Implementation
package router
import (
"github.com/cristiano-pacheco/bricks/pkg/http/server/chi"
"github.com/cristiano-pacheco/pingo/internal/modules/<module>/http/chi/handler"
)
type ResourceRouter struct {
handler *handler.ResourceHandler
}
func NewResourceRouter(h *handler.ResourceHandler) *ResourceRouter {
return &ResourceRouter{handler: h}
}
func (r *ResourceRouter) Setup(server *chi.Server) {
router := server.Router()
router.Get("/api/v1/resources", r.handler.ListResources)
router.Get("/api/v1/resources/{id}", r.handler.GetResource)
router.Post("/api/v1/resources", r.handler.CreateResource)
router.Put("/api/v1/resources/{id}", r.handler.UpdateResource)
router.Delete("/api/v1/resources/{id}", r.handler.DeleteResource)
}
Router Patterns
Custom Endpoints
Use POST with a verb suffix for non-CRUD state transitions. Use nested paths for sub-resources.
func (r *ResourceRouter) Setup(server *chi.Server) {
router := server.Router()
router.Get("/api/v1/resources", r.handler.ListResources)
router.Post("/api/v1/resources", r.handler.CreateResource)
router.Post("/api/v1/resources/{id}/activate", r.handler.ActivateResource)
router.Post("/api/v1/resources/{id}/deactivate", r.handler.DeactivateResource)
router.Get("/api/v1/resources/{id}/items", r.handler.ListResourceItems)
router.Post("/api/v1/resources/{id}/items", r.handler.AddResourceItem)
}
Route Groups (with middleware)
Use router.Group to scope middleware to a subset of routes without affecting others.
func (r *ResourceRouter) Setup(server *chi.Server) {
router := server.Router()
router.Get("/api/v1/resources", r.handler.ListResources)
router.Get("/api/v1/resources/{id}", r.handler.GetResource)
router.Group(func(r chi.Router) {
r.Use(middleware.Auth)
r.Post("/api/v1/resources", r.handler.CreateResource)
r.Put("/api/v1/resources/{id}", r.handler.UpdateResource)
r.Delete("/api/v1/resources/{id}", r.handler.DeleteResource)
})
}
Multiple Handlers
When a router logically owns routes across two related resources (e.g., a resource and its items), inject both handlers via the constructor.
type ResourceRouter struct {
resourceHandler *handler.ResourceHandler
itemHandler *handler.ItemHandler
}
func NewResourceRouter(
resourceHandler *handler.ResourceHandler,
itemHandler *handler.ItemHandler,
) *ResourceRouter {
return &ResourceRouter{
resourceHandler: resourceHandler,
itemHandler: itemHandler,
}
}
func (r *ResourceRouter) Setup(server *chi.Server) {
router := server.Router()
router.Get("/api/v1/resources", r.resourceHandler.ListResources)
router.Post("/api/v1/resources", r.resourceHandler.CreateResource)
router.Get("/api/v1/items", r.itemHandler.ListItems)
router.Post("/api/v1/items", r.itemHandler.CreateItem)
}
Fx Wiring
Add to internal/modules/<module>/fx.go. The fx.As(new(chi.Route)) and fx.ResultTags are required — they register the router into the routes group so the HTTP server discovers it automatically.
fx.Provide(
fx.Annotate(
router.NewResourceRouter,
fx.As(new(chi.Route)),
fx.ResultTags(`group:"routes"`),
),
),
Multiple routers in the same module:
fx.Provide(
fx.Annotate(
router.NewResourceRouter,
fx.As(new(chi.Route)),
fx.ResultTags(`group:"routes"`),
),
fx.Annotate(
router.NewItemRouter,
fx.As(new(chi.Route)),
fx.ResultTags(`group:"routes"`),
),
),
URL Path Conventions
- Version prefix:
/api/v1/ - Resource names: Plural nouns (
/resources,/contacts,/monitors) - Resource ID:
{id}path param (/resources/{id}) - Nested resources:
/resources/{id}/items - Nested with two IDs:
/resources/{resourceId}/items/{itemId} - Actions: Verb suffix for non-CRUD (
/resources/{id}/activate) - Bulk:
/resources/bulkwith appropriate HTTP method
HTTP Methods
GET: Retrieve (list or single)POST: Create, or trigger an actionPUT: Full updatePATCH: Partial updateDELETE: Remove
Naming Conventions
- Struct:
<Resource>Router(PascalCase) - Constructor:
New<Resource>Router - File:
<resource>_router.go(snake_case) - Handler methods: Match action —
ListResources,CreateResource,ActivateResource
Rules
- No standalone functions: When a file contains a struct with methods, do not add standalone functions. Use private methods on the struct instead.
- The struct holds only handler pointer(s) — no other state
- Constructor returns a pointer (
*ResourceRouter) Setupmethod signature is exactlySetup(server *chi.Server)— never deviate- Always call
server.Router()insideSetupto get the chi router - Every route must start with
/api/v1/ - Resource names in paths are plural nouns
- Fx wiring requires both
fx.As(new(chi.Route))andfx.ResultTags(\group:"routes"`)` - Imports: only
bricks/pkg/http/server/chiand the handler package (plus middleware if using route groups) - No comments in the file — the code is self-describing
- Run
make lintafter generating
Workflow
- Create
internal/modules/<module>/http/chi/router/<resource>_router.go - Define struct with handler field(s)
- Implement constructor
- Implement
Setup(server *chi.Server)with all routes - Add Fx wiring to
internal/modules/<module>/fx.go - Run
make lintandmake nilawayto ensure code quality and no nil pointer issues
Weekly Installs
7
Repository
cristiano-pache…ai-rulesFirst Seen
Feb 26, 2026
Security Audits
Installed on
opencode7
gemini-cli7
claude-code7
github-copilot7
codex7
amp7