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")))