Route bindings
Verb factories produce RouteBinding values that app.wire(binding, handler) consumes. Each carries the HTTP verb, path, optional schemas (params/query/body), per-route middleware, and OpenAPI metadata.
Factories
import { get, post, put, patch, del } from "@nwire/wires/http";
const listUsers = get("/users");
const getUser = get("/users/:id");
const createUser = post("/users");
const updateUser = put("/users/:id");
const patchUser = patch("/users/:id");
const deleteUser = del("/users/:id");With schemas
import { z } from "zod";
const getUser = get("/users/:id", {
params: z.object({ id: z.string() }),
});
const createUser = post("/users", {
body: z.object({
email: z.email(),
name: z.string().min(1).max(100),
}),
});
const listUsersFiltered = get("/users", {
query: z.object({
role: z.enum(["admin", "user"]).optional(),
limit: z.coerce.number().int().min(1).max(100).default(20),
}),
});
const updateUser = put("/users/:id", {
params: z.object({ id: z.string() }),
body: z.object({ name: z.string().optional() }),
});The handler's input is the merged + validated result of all three.
Per-route middleware
const purge = post("/admin/purge", {
middleware: [requireAdmin, logSensitive],
});Middleware runs in declaration order, before the handler.
OpenAPI metadata
const createUser = post("/users", {
body: UserInput,
openapi: {
operation: "CreateUser",
summary: "Create a user",
description: "Idempotent — POSTing the same email returns the existing row.",
tags: ["users"],
returns: [ok200User],
errors: [UserNotFound, EmailAlreadyTaken],
},
});Surfaced at /openapi.json when httpKoa({ openapi: { auto: true } }) is set; Scalar UI at /docs when docs: true.
Auth policy
The policy field on the underlying action carries an authz tag consumed by @nwire/auth and @nwire/rbac plugins. It's opaque to the framework — the authorizer decides what each tag means. When you wire an action's handler directly, the binding picks up the action's policy automatically.
Status codes
The default 2xx status is inferred from the handler return:
- plain value (object/array) → 200
null/undefined→ 204{ $kind: "response-instance", status, body }(fromdefineResource) →status{ $status, body }(legacy envelope) →$status
Type inference
const route = post("/users", {
body: z.object({ email: z.email() }),
});
// route is RouteBinding<{ email: string }>
// app.wire(route, handler) infers handler's input type