Routing & Handlers
Defining Routes
router := app.Router()
neutron.Get(router, "/users", listUsers)
neutron.Post(router, "/users", createUser)
neutron.Put(router, "/users/{id}", updateUser)
neutron.Patch(router, "/users/{id}", patchUser)
neutron.Delete(router, "/users/{id}", deleteUser)
Typed Handlers
Handlers are generic functions with automatic input extraction and output serialization:
type HandlerFunc[In, Out any] func(ctx context.Context, input In) (Out, error)
Path Parameters
type GetUserInput struct {
ID int64 `path:"id"`
}
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
func getUser(ctx context.Context, input GetUserInput) (User, error) {
// input.ID extracted from /users/{id}
return User{ID: input.ID, Name: "Alice"}, nil
}
neutron.Get(router, "/users/{id}", getUser)
Request Body
type CreateUserInput struct {
Name string `json:"name"`
Email string `json:"email"`
}
func createUser(ctx context.Context, input CreateUserInput) (User, error) {
// input.Name and input.Email parsed from JSON body
return User{ID: 1, Name: input.Name}, nil
}
neutron.Post(router, "/users", createUser)
No Input / No Output
// No input
func health(ctx context.Context, _ neutron.Empty) (map[string]string, error) {
return map[string]string{"status": "ok"}, nil
}
// No output (204 No Content)
func deleteUser(ctx context.Context, input DeleteInput) (neutron.Empty, error) {
return neutron.Empty{}, nil
}
Route Groups
Group routes with shared middleware:
api := router.Group("/api",
neutronauth.JWTMiddleware(secret),
)
neutron.Get(api, "/items", listItems)
neutron.Post(api, "/items", createItem)
admin := router.Group("/admin",
neutronauth.RBACMiddleware("admin"),
)
neutron.Get(admin, "/users", listAllUsers)
OpenAPI Metadata
Annotate routes for auto-generated OpenAPI docs:
neutron.Get(router, "/todos", listTodos,
neutron.WithSummary("List all todos"),
neutron.WithDescription("Returns paginated todo items"),
neutron.WithTags("todos"),
neutron.WithOperationID("listTodos"),
)
Error Handling
Return RFC 7807 Problem Details errors:
func getUser(ctx context.Context, input GetUserInput) (User, error) {
user, err := db.FindUser(input.ID)
if err != nil {
return User{}, neutron.ErrNotFound("user not found")
}
return user, nil
}
Error helpers: ErrBadRequest, ErrUnauthorized, ErrForbidden, ErrNotFound, ErrConflict, ErrValidation, ErrRateLimited, ErrInternal.
Middleware
Standard net/http middleware signature:
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
neutron.WriteError(w, r, neutron.ErrUnauthorized("missing token"))
return
}
next.ServeHTTP(w, r)
})
}
Built-in: Logger, Recover, RequestID, CORS, RateLimit, Timeout, Compress, OTel.
Static Files
router.Mount("/static", http.FileServer(http.Dir("./public")))