CLI Reference
add handler
Add an endpoint, TypeSpec operation plus handler method skeleton.
go tool gpsystem add handler <module> <surface> <operation> \
--method GET --path "/items/{id}"
| Flag | Default | Meaning |
|---|---|---|
--method | GET | one of GET, POST, PUT, PATCH, DELETE |
--path | / | route path relative to the surface base; {name} segments become path parameters |
--permission | none | RBAC permission for the operation; emits a @permission("...") decorator into the .tsp |
--policy | none | per-user policy name; emits a @policy("...") decorator into the .tsp; register the name in the surface's policy/policy.go |
--group | none (the --path's first literal segment) | the handler-file category, e.g. notifications; an empty group lands in the module's base handler file |
What it does
- Inserts a TypeSpec operation skeleton at the
gpsystem:operationsanchor in<surface>.tsp: path parameters declared, a@bodyparameter added for POST/PUT/PATCH, return type stubbed. With--permission/--policythe matching decorators are added and the response union widens withUnauthorized | Forbidden; enforcement happens in the generated middleware (see auth, rbac & policy). - Adds the handler method to a category file under
internal/modules/<module>/surfaces/<surface>/http/:<module>_handler.gofor the base category,<module>_<group>_handler.gootherwise. The group defaults to the--path's first non-parameter segment (/notifications/{id}/read->notifications); override it with--group. The category's first operation creates the file; later ones are inserted as a new method at the file's trailing// gpsystem:handlersanchor. The stub returns 501 Not Implemented as a problem document, so the endpoint is immediately visible and testable.handler.gostays theHandlerstruct,Newand//go:generatehome: it carries no handler methods.
The workflow
go tool gpsystem add handler news api getNews --method GET --path "/items/{id}"
mise run generate # materializes gen.GetNewsRequest / gen.GetNewsResponse
getNews groups under items because of its /items/{id} path:
internal/modules/news/surfaces/api/http/news_items_handler.go. A later operation in
the same group (e.g. deleteNews --path "/items/{id}") lands in the same
file, next to the existing method.
Then replace the stub body: call the service and wrap its error at the boundary, the same pattern the add surface skeleton generates:
// internal/modules/news/surfaces/api/http/news_items_handler.go
func (h *Handler) GetNews(ctx context.Context, req gen.GetNewsRequest) (gen.GetNewsResponse, error) {
item, err := h.svc.GetByID(ctx, req.Id) // add the service method
if err != nil {
return nil, errs.Wrap(err, "api: get news") // carries a stack; httperr renders it
}
return mapper.ToGetNewsResponse(item), nil // add the mapper
}
Between
add handler and mise run generate the project does not compile: the handler references generated types that do not exist yet. That is expected; the generate step closes the gap.Refine the operation in the .tsp file (models, error unions, pagination) and re-run mise run generate: the strict interface keeps your handler honest at compile time.