Routing
Declarative route definitions with type-safe params and automatic context injection
Route Builders
Gello provides a fluent route builder API that creates typed route definitions. Routes are just data — arrays of route objects that get registered with your app.
import { route } from "@gello/core-adapters-node"
// Define routes as data
export const routes = [
route.get("/", homeHandler),
route.get("/health", healthCheck),
route.get("/users", listUsers),
route.get("/users/:id", getUser),
route.post("/users", createUser),
route.patch("/users/:id", updateUser),
route.delete("/users/:id", deleteUser),
] as constRoute Parameters
Route parameters are automatically extracted and injected into your handler's context. Use the getParam helper to access them type-safely.
import { getParam } from "@gello/core-domain-routing"
const getUser = Effect.gen(function* () {
// Extract :id from the path
const id = yield* getParam("id")
const repo = yield* UserRepo
const user = yield* repo.findById(id)
if (!user) {
return HttpServerResponse.empty({ status: 404 })
}
return HttpServerResponse.json(user)
})Query Parameters
Query parameters are also available via context. Use typed helpers for common conversions.
import {
getQuery,
getQueryAsNumber,
getQueryAsBoolean
} from "@gello/core-domain-routing"
const listUsers = Effect.gen(function* () {
// ?page=2&limit=20&active=true
const page = yield* getQueryAsNumber("page", 1) // defaults to 1
const limit = yield* getQueryAsNumber("limit", 20) // defaults to 20
const active = yield* getQueryAsBoolean("active") // Option<boolean>
const repo = yield* UserRepo
const users = yield* repo.list({ page, limit, active })
return HttpServerResponse.json(users)
})Registering Routes
Routes are registered with your app using the routes() method. The app automatically injects RouteParams, QueryParams, and HttpServerRequest into each handler's context.
import { createApp, runApp } from "@gello/core-adapters-node"
import { routes } from "./routes"
const app = createApp({ port: 3000 })
.use(cors({ origins: "*" }))
.routes(routes)
runApp(app, AppLayer)Route Groups
Organize related routes by defining them in separate files and combining them.
// routes/users.ts
export const userRoutes = [
route.get("/users", listUsers),
route.get("/users/:id", getUser),
route.post("/users", createUser),
] as const
// routes/posts.ts
export const postRoutes = [
route.get("/posts", listPosts),
route.get("/posts/:id", getPost),
] as const
// routes/index.ts
export const routes = [
...apiRoutes,
...userRoutes,
...postRoutes,
] as constMiddleware per Route
Apply middleware to specific routes by wrapping handlers.
const withAuth = <R>(handler: Effect.Effect<HttpServerResponse, RouteError, R>) =>
pipe(
Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest
const token = request.headers.authorization?.replace("Bearer ", "")
if (!token) {
return HttpServerResponse.empty({ status: 401 })
}
const user = yield* verifyToken(token)
return yield* handler.pipe(Effect.provideService(CurrentUser, user))
})
)
export const routes = [
route.get("/public", publicHandler),
route.get("/me", withAuth(getProfile)),
route.post("/settings", withAuth(updateSettings)),
] as constError Handling
Wrap routes with error handlers to convert domain errors to HTTP responses.
const handleError = (error: RouteError) =>
Match.value(error).pipe(
Match.tag("NotFoundError", (e) =>
HttpServerResponse.json({ error: e.message }, { status: 404 })
),
Match.tag("ValidationError", (e) =>
HttpServerResponse.json({ error: e.message }, { status: 400 })
),
Match.orElse(() =>
HttpServerResponse.json({ error: "Internal error" }, { status: 500 })
)
)
const handle = <R>(handler: Effect.Effect<HttpServerResponse, RouteError, R>) =>
pipe(handler, Effect.catchAll(handleError))
export const routes = [
route.get("/users/:id", handle(getUser)),
route.post("/users", handle(createUser)),
] as constCLI: List Routes
Use the Gello CLI to view all registered routes in your application.
pnpm gello route:listThis displays a beautiful TUI with all routes, their methods, paths, and handlers — grouped by path prefix with color-coded HTTP methods.