Api & Route
HTTP routes on API Gateway, one Lambda function per route, with Zod validation built in.
Declaring an API
import { Api } from "@vorynza/wisp";
export const api = new Api("main", {
cors: { origins: ["*"] },
});| Option | Type | Description |
|---|---|---|
cors | { origins: string[] } | Optional. Enables CORS on the API with the given allowed origins. |
customDomain | string | Optional. Reserved for a future milestone — not implemented yet. |
Declaring a route
Call get, post, put, patch, or delete on an Api instance. Each call must be assigned directly to an
exported const — that's how the compiler finds it:
import { z } from "zod";
import { api } from "./app.ts";
import { orders } from "./stores/orders.ts";
export const createOrder = api.post(
"/orders",
{
body: z.object({ sku: z.string(), qty: z.number().int().positive() }),
},
async ({ body }) => {
const order = await orders.put({ id: crypto.randomUUID(), ...body, status: "pending" });
return { status: 201, body: order };
},
);Route options
| Option | Type | Description |
|---|---|---|
body | z.ZodType | Optional Zod schema. Requests that fail validation are rejected before your handler runs. |
query | z.ZodType | Optional schema for the query string. |
params | z.ZodType | Optional schema for path parameters (/orders/:id). |
memory | number | Lambda memory in MB. Defaults to 1024. |
timeout | number | Lambda timeout in seconds. Defaults to 10. |
The handler
Your handler receives one argument with the request already parsed and validated:
interface HandlerContext<TBody, TQuery, TParams> {
body: TBody;
query: TQuery;
params: TParams;
headers: Record<string, string>;
}...and returns a response — synchronously or as a Promise:
interface RouteResponse {
status: number;
body?: unknown;
headers?: Record<string, string>;
}Path parameters
export const getOrder = api.get("/orders/:id", { params: z.object({ id: z.string() }) }, async ({ params }) => {
const order = await orders.get({ id: params.id });
if (!order) return { status: 404, body: { error: "not_found" } };
return { status: 200, body: order };
});What this deploys to
Each route compiles to its own AWS::Lambda::Function, wired to a shared AWS::ApiGatewayV2::Api via a dedicated
AWS::ApiGatewayV2::Route and Integration. There is no runtime router — API Gateway invokes the exact function for
the matched route directly. Each route's Lambda gets its own AWS::IAM::Role, scoped to only the resources that
specific handler references (see IAM derivation).

