7.1 KiB
Handler
Handlers in SigNoz are responsible for exposing module functionality over HTTP. They are thin adapters that:
- Decode incoming HTTP requests
- Call the appropriate module layer
- Return structured responses (or errors) in a consistent format
- Describe themselves for OpenAPI generation
They are not the place for complex business logic; that belongs in modules (for example, pkg/modules/user, pkg/modules/session, etc).
How are handlers structured?
At a high level, a typical flow looks like this:
- A
Handlerinterface is defined in the module (for example,user.Handler,session.Handler,organization.Handler). - The
apiserverprovider wires those handlers into HTTP routes using Gorillamux.Router.
Each route wraps a module handler method with the following:
- Authorization middleware (from
pkg/http/middleware) - A generic HTTP
handler.Handler(frompkg/http/handler) - An
OpenAPIDefthat describes the operation for OpenAPI generation
For example, in pkg/apiserver/signozapiserver:
if err := router.Handle("/api/v1/invite", handler.New(
provider.authZ.AdminAccess(provider.userHandler.CreateInvite),
handler.OpenAPIDef{
ID: "CreateInvite",
Tags: []string{"users"},
Summary: "Create invite",
Description: "This endpoint creates an invite for a user",
Request: new(types.PostableInvite),
RequestContentType: "application/json",
Response: new(types.Invite),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusCreated,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
},
)).Methods(http.MethodPost).GetError(); err != nil {
return err
}
In this pattern:
provider.userHandler.CreateInviteis a handler method.provider.authZ.AdminAccess(...)wraps that method with authorization checks and context setup.handler.Newconverts it into an HTTP handler and wires it to OpenAPI via theOpenAPIDef.
How to write a new handler method?
When adding a new endpoint:
- Add a method to the appropriate module
Handlerinterface. - Implement that method in the module.
- Register the method in
signozapiserverwith the correct route, HTTP method, auth, andOpenAPIDef.
1. Extend an existing Handler interface or create a new one
Find the module in pkg/modules/<name> and extend its Handler interface with a new method that receives an http.ResponseWriter and *http.Request. For example:
type Handler interface {
// existing methods...
CreateThing(rw http.ResponseWriter, req *http.Request)
}
Keep the method focused on HTTP concerns and delegate business logic to the module.
2. Implement the handler method
In the module implementation, implement the new method. A typical implementation:
- Extracts authentication and organization context from
req.Context() - Decodes the request body into a
types.*struct using thebindingpackage - Calls module functions
- Uses the
renderpackage to write responses or errors
func (h *handler) CreateThing(rw http.ResponseWriter, req *http.Request) {
// Extract authentication and organization context from req.Context()
claims, err := authtypes.ClaimsFromContext(req.Context())
if err != nil {
render.Error(rw, err)
return
}
// Decode the request body into a `types.*` struct using the `binding` package
var in types.PostableThing
if err := binding.JSON.BindBody(req.Body, &in); err != nil {
render.Error(rw, err)
return
}
// Call module functions
out, err := h.module.CreateThing(req.Context(), claims.OrgID, &in)
if err != nil {
render.Error(rw, err)
return
}
// Use the `render` package to write responses or errors
render.Success(rw, http.StatusCreated, out)
}
3. Register the handler in signozapiserver
In pkg/apiserver/signozapiserver, add a route in the appropriate add*Routes function (addUserRoutes, addSessionRoutes, addOrgRoutes, etc.). The pattern is:
if err := router.Handle("/api/v1/things", handler.New(
provider.authZ.AdminAccess(provider.thingHandler.CreateThing),
handler.OpenAPIDef{
ID: "CreateThing",
Tags: []string{"things"},
Summary: "Create thing",
Description: "This endpoint creates a thing",
Request: new(types.PostableThing),
RequestContentType: "application/json",
Response: new(types.GettableThing),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusCreated,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
},
)).Methods(http.MethodPost).GetError(); err != nil {
return err
}
4. Update the OpenAPI spec
Run the following command to update the OpenAPI spec:
go run cmd/enterprise/*.go generate openapi
This will update the OpenAPI spec in docs/api/openapi.yml to reflect the new endpoint.
How does OpenAPI integration work?
The handler.New function ties the HTTP handler to OpenAPI metadata via OpenAPIDef. This drives the generated OpenAPI document.
- ID: A unique identifier for the operation (used as the
operationId). - Tags: Logical grouping for the operation (for example,
"users","sessions","orgs"). - Summary / Description: Human-friendly documentation.
- Request / RequestContentType:
Requestis a Go type that describes the request body or form.RequestContentTypeis usually"application/json"or"application/x-www-form-urlencoded"(for callbacks like SAML).
- Response / ResponseContentType:
Responseis the Go type for the successful response payload.ResponseContentTypeis usually"application/json"; use""for responses without a body.
- SuccessStatusCode: The HTTP status for successful responses (for example,
http.StatusOK,http.StatusCreated,http.StatusNoContent). - ErrorStatusCodes: Additional error status codes beyond the standard ones automatically added by
handler.New. - SecuritySchemes: Auth mechanisms and scopes required by the operation.
The generic handler:
- Automatically appends
401,403, and500toErrorStatusCodeswhen appropriate. - Registers request and response schemas with the OpenAPI reflector so they appear in
docs/api/openapi.yml.
See existing examples in:
addUserRoutes(for typical JSON request/response)addSessionRoutes(for form-encoded and redirect flows)
What should I remember?
- Keep handlers thin: focus on HTTP concerns and delegate logic to modules/services.
- Always register routes through
signozapiserverusinghandler.Newand a completeOpenAPIDef. - Choose accurate request/response types from the
typespackages so OpenAPI schemas are correct.