createRequestHandler
createRequestHandler is the primary server entry point. It takes the ApiDefinition produced by defineApi and returns an async function that maps a standard Web API Request to a Response. Because it works with the Web-standard Request/Response objects, the returned handler drops straight into Astro, Next.js, Hono, Bun, Deno, Cloudflare Workers, or any other Web-standard runtime — no adapter required.
import { createRequestHandler } from "@toapi/server";import { api } from "./api";
const handler = createRequestHandler(api, { basePath: "/api",});
// handler: (req: Request) => Promise<Response>See the framework guides for how to wire the handler into a specific runtime: Astro, Next.js, Hono, Bun.
Signature
Section titled “Signature”function createRequestHandler( api: ApiDefinition<Routes>, options?: { basePath?: string; defaultTTL?: number; },): (req: Request) => Promise<Response>;Parameters
Section titled “Parameters”Type: ApiDefinition
The API definition object returned by defineApi, containing the complete map of routes, the cache instance, the optional OpenAPI info, and the optional logger.
options
Section titled “options”| Option | Type | Default | Description |
|---|---|---|---|
basePath |
string |
"" |
The root path under which all routes are mounted. Must match the URL prefix where you serve the handler (e.g. /api). It is prepended to every route path and to the built-in routes. |
defaultTTL |
number |
1209600 (14 days) |
The default time-to-live, in seconds, applied to cached responses whose handler set cache without an explicit ttl. |
Path matching
Section titled “Path matching”Route paths registered with defineApi().route() support three segment kinds, compiled to a regular expression at startup:
- Static segments —
/usersmatches exactly. - Named parameters —
:idin/users/:idmatches a single path segment and is exposed viareq.params().id. - Wildcards —
*matches everything remaining (including slashes).*namecaptures the remainder into a named parameter, so/files/*pathexposesreq.params().path.
Parameter values are URL-decoded before validation.
Built-in routes
Section titled “Built-in routes”The handler responds to two reserved routes under basePath before matching your own routes:
| Route | Condition | Behaviour |
|---|---|---|
<basePath>/__tapi/invalidations |
always | Opens the long-polling invalidation stream via streamRevalidatedTags. Clients and service workers connect here to receive revalidated tags. |
<basePath>/__tapi/openapi.json |
only when oas is set on defineApi |
Serves the generated OpenAPI document (see generateOpenAPISchema). The document is generated once and cached in memory. |
Request lifecycle
Section titled “Request lifecycle”For each request the handler resolves the matching route and dispatches by HTTP method:
- Prepare the request. The incoming
Requestis augmented into aTRequest, giving lazy, validated accessors forparams(),query(),cookies(),data(), andinvalidate(). - Authorize. The handler’s
authorizefunction runs. If it returns a falsy value, the request is rejected with401 Unauthorized. The resolved value is available viareq.auth(). - Serve from cache (GET/HEAD only). If a
cacheis configured on the API and a fresh entry exists for the request URL, the stored response is served without invoking the handler.HEADrequests receive the cached headers with no body. - Run the handler. The handler function executes and returns a
TResponse. If aresponseschema is defined, the returned data is validated against it. - Cache the response (GET/HEAD only). If the response carries
cacheoptions andreq.auth()was not called during handling, the response is stored using the response’stagsandttl(falling back todefaultTTL). - Invalidate tags (mutations). For
POST,PUT,PATCH, andDELETE, if the response carriescache.tags, those tags are invalidated via the cache’s pub/sub. When a session cookie is present, the originating client ID is forwarded so a client does not receive its own invalidation echo.
Error handling
Section titled “Error handling”Errors thrown while handling a request are logged (via the Logger supplied to defineApi, or console.error) and converted into responses:
| Thrown value | Response |
|---|---|
ZodError (validation failure) |
400 Bad Request, body is the array of Zod issues, Content-Type: application/json+zodissues. |
HttpError |
The error’s status, body { message, data }, Content-Type: application/json+httperror. |
| Anything else | 500 Internal Server Error. |
Requests that match no route, or match a route with no handler for the given method, return 404 Not Found.
Related
Section titled “Related”defineApi— build the API definition passed in here.createLocalClient— wrapscreateRequestHandlerto call handlers in-process.- Caching Strategies — how the cache layers cooperate.