Astro Setup
This guide explains how to integrate Toapi with Astro API routes.
Installation
Section titled “Installation”npm install @toapi/server @toapi/client zod# orpnpm add @toapi/server @toapi/client zod1. Define your API
Section titled “1. Define your API”Create a central file to define your API structure.
import { defineApi } from "@toapi/server";
export const api = defineApi();// We'll add routes here later// .route("/hello", import("./routes/hello"))2. Create the route handler
Section titled “2. Create the route handler”Astro uses file-based routing. To handle all requests under /api, create a catch-all route file at src/pages/api/[...tapi].ts.
import { createRequestHandler } from "@toapi/server";import { api } from "../../api";import type { APIRoute } from "astro";
const handler = createRequestHandler(api, { basePath: "/api",});
// The ALL export handles all HTTP methods (GET, POST, etc.)export const ALL: APIRoute = ({ request }) => { return handler(request);};3. Create the client
Section titled “3. Create the client”Create a client for browser-side usage.
import { createFetchClient } from "@toapi/client";import type { api } from "./api";
export const client = createFetchClient<typeof api.routes>("/api");4. Add a route
Section titled “4. Add a route”Create a route handler file. For example, src/routes/hello.ts:
import { defineHandler, TResponse } from "@toapi/server";
export const GET = defineHandler({ authorize: () => true }, async () => { return TResponse.json({ message: "Hello from Toapi!" });});Register it in src/api.ts:
import { defineApi } from "@toapi/server";
export const api = defineApi().route("/hello", import("./routes/hello"));5. Usage
Section titled “5. Usage”Client-side usage
Section titled “Client-side usage”Use the client in your <script> tags or within UI framework components (React, Vue, Svelte, etc.) that run on the client.
<button id="fetch-btn">Fetch Data</button><div id="result"></div>
<script> import { client } from "../client";
const btn = document.getElementById("fetch-btn"); const result = document.getElementById("result");
btn?.addEventListener("click", async () => { const data = await client.hello.get(); if (result) result.innerText = data.message; });</script>Server-side usage (frontmatter)
Section titled “Server-side usage (frontmatter)”When fetching data inside Astro component frontmatter (which runs on the server during build or SSR), use createLocalClient to call handlers directly without an HTTP round-trip.
First, set up a server client helper:
import { createLocalClient } from "@toapi/server";import { api } from "./api";
export const serverClient = createLocalClient(api);Then use it in your Astro pages:
---import { serverClient } from "../server-client";
const data = await serverClient.hello.get();---
<h1>{data.message}</h1>6. Revalidation stream
Section titled “6. Revalidation stream”To enable tag-based revalidation across all cache layers, expose the invalidation stream. defineApi automatically creates a PubSub instance, so no extra setup is needed for single-host deployments.
The catch-all handler from step 2 already serves the stream at /api/__tapi/invalidations. If you prefer a dedicated route, add one at src/pages/api/revalidate.ts:
import { streamRevalidatedTags } from "@toapi/server";import { api } from "../../api";import type { APIRoute } from "astro";
export const GET: APIRoute = () => { return streamRevalidatedTags({ cache: api.cache, });};For setting up a service worker that connects to this endpoint, see the @toapi/worker package. For details on how the cache layers interact, see Caching Strategies.