Skip to content

Routing

The λ Cosmos router builds on Go’s http.ServeMux and adds middleware composition, route groups, and generics support. In the framework package, the router is pre-configured with the Cosmos Handler type (handlers that return error).

type Router = router.Router[Handler]
type Middleware = router.Middleware[Handler]
// which expands to: func(Handler) Handler
import "github.com/studiolambda/cosmos/framework"
app := framework.New()

This returns a *framework.Router, which is an alias for router.Router[framework.Handler]. It implements http.Handler so you can pass it directly to http.ListenAndServe.

Register routes using HTTP method helpers:

app.Get("/users", listUsers)
app.Post("/users", createUser)
app.Get("/users/{id}", getUser)
app.Put("/users/{id}", updateUser)
app.Delete("/users/{id}", deleteUser)

All standard HTTP methods are available: Get, Post, Put, Patch, Delete, Head, Options, Connect, Trace. There’s also Any for catching all methods and Method/Methods for custom method lists:

app.Any("/health", healthCheck)
app.Method("PURGE", "/cache/{key}", purgeCache)
app.Methods([]string{"GET", "HEAD"}, "/status", statusHandler)

Path parameters use Go’s standard {name} syntax:

app.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
// ...
})

Wildcard parameters capture the rest of the path:

app.Get("/files/{path...}", func(w http.ResponseWriter, r *http.Request) error {
path := r.PathValue("path") // e.g. "images/photo.jpg"
// ...
})

The router automatically handles trailing slash normalization. When you register /users, both /users and /users/ will match. This is handled transparently — you don’t need to register both patterns.

Groups let you organize routes under a common prefix and share middleware:

api := app.Group("/api")
api.Use(authMiddleware)
api.Get("/users", listUsers) // matches /api/users
api.Get("/users/{id}", getUser) // matches /api/users/{id}

Groups can be nested:

v1 := api.Group("/v1")
v1.Get("/items", listItemsV1) // matches /api/v1/items
v2 := api.Group("/v2")
v2.Get("/items", listItemsV2) // matches /api/v2/items

For inline group definitions, use Grouped:

app.Grouped("/admin", func(admin *framework.Router) {
admin.Use(adminAuth)
admin.Get("/dashboard", dashboard)
admin.Get("/settings", settings)
})

This is equivalent to calling Group followed by the route registrations, but keeps the group scope visually contained.

Middleware wraps handlers to add cross-cutting behavior. A Middleware is a function that takes a handler and returns a new handler:

type Middleware = func(Handler) Handler

Apply middleware to a router or group with Use:

app.Use(middleware.Recover())
app.Use(middleware.Logger(logger))

Middleware runs in the order it’s registered. Apply middleware to a specific route by wrapping it inline with With:

app.Get("/admin", app.With(adminAuth)(adminHandler))

See the Middleware page for the full list of built-in middleware.

The Clone method creates a shallow copy of a router with the same prefix but independent middleware:

clone := app.Clone()
clone.Use(extraMiddleware)
// The original app is unaffected

If you don’t need the full framework (error-returning handlers, hooks), you can use the router module directly with any http.Handler implementation:

import "github.com/studiolambda/cosmos/router"
r := router.New[http.HandlerFunc]()
r.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello"))
})
http.ListenAndServe(":8080", r)

The router is generic over any type that implements http.Handler, so you can use it with custom handler types in projects that don’t use the Cosmos framework layer.