Kizaki
ReferenceInspire

Routes

Declare HTTP endpoints in Inspire and route requests into server handlers.

Routes define the HTTP surface of your app for callers outside the generated-client path. Use them for webhooks, partner integrations, machine-to-machine APIs, and external systems that talk to your app over plain HTTP.

For your own frontend, exposed functions plus @kizaki/client are the better default. Routes are the escape hatch when the caller is not your generated client.

Basic Shape

routes("/v1", auth: apiKey(read)) {
  GET  /projects -> listProjectsRoute
  POST /projects -> createProjectRoute
}

A route declaration specifies:

  • Path
  • HTTP method
  • Handler function
  • Auth mode

Grouping, Prefixes, And Overrides

Route groups share a prefix and default settings. Per-route overrides appear only where behavior changes.

routes("/v1", auth: apiKey(read), rateLimit: 1000/minute) {
  GET /projects -> listProjectsRoute
  GET /projects/:projectId -> getProjectRoute

  POST /projects -> createProjectRoute (auth: apiKey(write))
  DELETE /projects/:projectId -> deleteProjectRoute (auth: apiKey(write))

  routes("/admin", auth: apiKey(admin)) {
    GET /users -> listUsersRoute
    DELETE /users/:userId -> deleteUserRoute
  }
}

Auth Modes

session

Use when the caller is a signed-in user.

routes("/app", auth: session) {
  GET /me -> getCurrentUserRoute
  POST /projects -> createProjectRoute
}

Provides normal user identity, policies, and tenant scoping.

apiKey(scope)

Use for machine-to-machine access.

routes("/v1", auth: apiKey(read)) {
  GET /projects -> listProjectsRoute
  GET /projects/:projectId -> getProjectRoute
}

The scope name comes from the apiKeys block. Use for partner APIs, ingestion endpoints, and automation.

none

Use only when the route has its own verification model, such as a signed webhook.

routes("/webhooks", auth: none) {
  POST /stripe -> stripeWebhookRoute
}

The handler must verify the caller independently.

Handler Model

The schema owns the routing table. Your server code owns the handler logic.

import { Project } from "@kizaki/schema";
import {
  RouteRequest,
  badRequest,
  created,
  insert,
  ok,
  query,
  select,
} from "@kizaki/sdk";

export async function listProjectsRoute(_req: RouteRequest) {
  const rows = await query(
    select(Project)
      .fields(Project.id, Project.name)
      .orderBy("createdAt", "desc"),
  );

  return ok(rows);
}

export async function createProjectRoute(req: RouteRequest) {
  const input = req.body<{ name?: string }>();

  if (!input?.name) {
    return badRequest("name is required");
  }

  const [project] = await query(
    insert(Project)
      .values({ name: input.name })
      .returning(),
  );

  return created(project);
}

Routes are thin HTTP boundaries. Translate HTTP concerns into application workflows; keep business logic in reusable server code.

Request Surface

export async function getProjectRoute(req: RouteRequest) {
  const projectId = req.params.projectId;
  const format = req.query.format;
  const method = req.method;
  const path = req.path;
  const requestId = req.headers["x-request-id"];
  const rawBody = req.rawBody;
  const body = req.body<{ includeArchived?: boolean }>();

  return ok({ projectId, format, method, path, requestId, rawBodyLength: rawBody.length, body });
}
PropertyUse
req.paramsPath parameters like :projectId
req.queryQuery-string values
req.headersAuth signatures and integration metadata
req.rawBodyWebhook signature verification
req.body<T>()JSON request payloads

Response Helpers

import {
  badRequest,
  created,
  error,
  forbidden,
  noContent,
  notFound,
  ok,
  redirect,
  unauthorized,
} from "@kizaki/sdk";

ok({ id: "p_123" });
created({ id: "p_123" });
noContent();
redirect("/login");
badRequest("invalid payload");
unauthorized("not authenticated");
forbidden("missing scope");
notFound("project not found");
error(422, "unprocessable request");

Webhooks

Webhook routes use auth: none with handler-level verification.

routes("/webhooks", auth: none) {
  POST /github -> githubWebhookRoute
}
import { RouteRequest, badRequest, ok } from "@kizaki/sdk";

export async function githubWebhookRoute(req: RouteRequest) {
  const signature = req.headers["x-hub-signature-256"];

  if (!signature) {
    return badRequest("missing signature");
  }

  // Verify req.rawBody with the provider SDK or shared helper here.
  // Then hand off to normal application logic.

  return ok({ received: true });
}
  • Keep route declarations short and structural
  • Group by path prefix and auth model
  • Use apiKey(scope) for machine access
  • Reserve none for webhook-style verification flows
  • Use exposed functions and the generated client for your own frontend
  • Keep business logic in reusable server code, not inline in handlers

Related docs:

On this page