Server
Build a context-first HTTP application with explicit routing, middleware, actions, realtime, and probes.
Example
Build the HTTP surface from a router and context-first handlers; bind input and return an explicit response helper from the same boundary.
import { createRouter, created } from '@askrjs/server';
export const router = createRouter().post('/projects', async (context) => {
const input = await context.bind<CreateProjectInput>();
const project = await projects.create(input);
return created(project);
});Application and router
createServerApp({ router }) is the canonical low-level HTTP composition path. Add other cross-cutting options to the same object as needed. It produces a ServerApp whose only contract is fetch(request): Promise<Response> — nothing Node-, Bun-, or Deno-specific leaks into the return type. The router itself owns path matching, per-route middleware, and WebSocket upgrade wiring; the app wraps it with cross-cutting options like error handling and probe checks. The direct Router overload and raw routes option remain compatibility boundaries for existing integrations, not peer application guidance.
Context-first handlers
Every handler and every Middleware receives a single ServerContext argument instead of separate request/response objects — request, url, params, headers, query, state, auth, and an AbortSignal all live on ctx. Dependencies like a database client are deliberately not part of that context; the README shows creating them at your composition root and passing them into route registration functions, keeping ctx reserved for request-scoped data. Because the context also carries every response helper (ctx.ok, ctx.notFound, and so on), a handler can build its whole response without importing anything else.
Response helpers
ctx.ok(), ctx.notFound(), ctx.created(), ctx.problem(), and the rest of the status-code helpers are methods on ServerContext, so a handler returns a Response by calling ctx.<name>(value) rather than constructing new Response(...) by hand. The same functions — json, text, redirect, setCookie, clearCookie, challenge, and friends — are also exported standalone from @askrjs/server for use outside a request context, such as inside an onError handler. setCookie() and clearCookie() take a Response and return a modified one; per the README they only touch response headers and have no opinion on session storage or credential validation.
Production boundary
ServerAppOptions accepts an onError(error, context) hook that runs for an unhandled exception a handler or middleware throws, so you control what most errors turn into instead of leaking a stack trace to the client — but it's the fallback, not the only path: a few framework-recognized failures (an oversized request body, a malformed path parameter, a ctx.bind() failure) are converted to a fixed problem+json response before onError is ever consulted, so don't rely on it to customize those specific cases. The OpenAPI layer defaults to validateResponses: false and only turns response validation on when you explicitly opt in outside production, because checking every response against its schema costs real time on every request. The package is still published pre-1.0 (0.2.x), so pin versions and review release notes before moving a production app between versions.