HTTP Routing
Use http routing with the current published Askr packages.
@askrjs/server/router0.0.3
How to use http routing
Build the HTTP surface from a router and context-first handlers; bind input and return an explicit response helper from the same boundary.
- Import the published @askrjs/server/router entrypoint.
- Keep http routing configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { createRouter, created } from '@askrjs/server';
export const router = createRouter().post('/projects', async (context) => {
const input = await context.bind<CreateProjectInput>();
const project = await context.dependencies.projects.create(input);
return created(project);
});Create a router
createRouter() from @askrjs/server/router (also re-exported from the package root) returns a Router with no routes registered yet. Router carries the same verb methods as RouteBuilder — get, post, put, patch, delete, options, head, trace, connect, and ws — plus use(...middleware) and read-only routes and middleware arrays you can inspect after registration. Pass the finished router straight into createServerApp(router), or nest it inside a ServerAppOptions object alongside other cross-cutting config.
Define routes
Each HTTP verb has its own method — router.get(path, handler), router.post(...), and so on through trace and connect — plus a generic router.route(method, path, handler, options) that accepts a single verb or an array of them. defineRoutes(definition) is the alternative entry point: instead of mutating a Router, you get a RouteBuilder inside a callback and collect the results into a plain ApiRoute[], which is useful when you want route definitions as data before deciding how to mount them. Both paths accept the same ApiRouteOptions — auth and per-route middleware — as the handler's third argument.
Path parameters
Route paths use `{name}` for a single segment and `{*name}` for a named wildcard that captures the rest of the path — the README's example is router.get("/objects/{*key}", ctx => ctx.ok({ key: ctx.params.key })). Because paths are typed as const string literals, PathParams<Path> extracts the parameter names at compile time, so ctx.params is fully typed inside the handler without a separately written params interface. Values in ctx.params are always decoded strings, and the binding model treats them as authoritative — they win over a same-named query or body value when ctx.bind() merges everything together.
Route composition
Router methods return the Router itself, so calls chain — router.use(requestId()).get(...).post(...) — and use(...middleware) can be called more than once to layer cross-cutting concerns before route-specific ones run. Because route registration is usually wrapped in a plain function, like the README's registerUserRoutes(router, deps), a large API gets built by writing one function per feature area and calling each with the same router and a narrowed slice of dependencies. ServerAppOptions also accepts a standalone routes array alongside router, so ApiRoute[] produced by defineRoutes() can be merged in without going through a Router instance at all.