Skip to content

@toapi/server

@toapi/server is the server half of the Toapi stack. You use it to define your API surface, implement request handlers, and turn them into a request handler that runs on any Web-standard runtime. The API definition doubles as the single source of truth for the fully-typed @toapi/client — client and server share the same types with no code generation and no build step.

Terminal window
npm install @toapi/server zod

zod is a peer dependency used for request and response validation.

  • Type-safe by construction — the shape you build with defineApi is inferred into a single TypeScript type that the client consumes directly. No codegen, no compile step.
  • Auth by default — every handler defined with defineHandler must supply an authorize function, making it nearly impossible to ship an unauthenticated endpoint by accident.
  • Built-in validation — request params, query, and body, plus the response, are validated with Zod. Define the schema once and get types and runtime validation together.
  • Automatic OpenAPI — an OpenAPI 3.1 document is generated from your route definitions. Pass oas: { title, version } to defineApi to serve it at <basePath>/__tapi/openapi.json.
  • Tag-based caching — responses can carry cache tags and a ttl, driving invalidation across the server, service worker, and client cache layers.

Define the API surface in a shared file that both the server and the client import:

src/api.ts
import { defineApi } from "@toapi/server";
export const api = defineApi().route("/hello", import("./routes/hello"));

Implement a handler:

src/routes/hello.ts
import { defineHandler, TResponse } from "@toapi/server";
import { z } from "zod";
export const GET = defineHandler(
{
authorize: () => true,
response: z.object({ message: z.string() }),
},
async () => {
return TResponse.json({ message: "Hello, world!" });
},
);

Turn the API into a request handler and mount it on your runtime of choice:

src/server.ts
import { createRequestHandler } from "@toapi/server";
import { api } from "./api";
export const handler = createRequestHandler(api, { basePath: "/api" });

Consume it from the browser with the shared types — see @toapi/client:

// Client usage
import { createFetchClient } from "@toapi/client";
import type { api } from "./api";
const client = createFetchClient<typeof api.routes>("/api");
const { message } = await client.hello.get();
  • Astro — mount Toapi on Astro server endpoints.
  • Next.js — integrate Toapi with the App Router.
  • Hono — mount Toapi routes on a Hono server.
  • Bun — mount Toapi routes on a native Bun server.