--- title: 'API routes' slug: api-routes --- ## API routes `Mochi.api()` creates JSON API endpoints. The handler receives a `MochiApiEvent` with `method`, `request`, `url`, and `locals`: ```ts import { error } from "./mochi-framework/utils"; "/health": Mochi.api(({ method }) => Response.json({ status: "ok", method }), ), "/add": Mochi.api(async ({ method, request }) => { if (method !== "POST") error(405, "Method Not Allowed"); const { a, b } = await request.json(); return Response.json({ result: a + b }); }), ``` ### Error responses API routes return a standard JSON envelope for all errors — they never render the HTML error page, and `handleError` is not called for them. Use `apiError(status, message)` to build the envelope response: ```ts import { apiError } from 'mochi-framework'; Mochi.api(async ({ request }) => { const body = await request.json().catch(() => null); if (!body) return apiError(400, 'Invalid JSON'); return Response.json({ ok: true }); }); // bad body → 400 { "error": { "message": "Invalid JSON", "status": 400 } } ``` `apiError` returns a `Response` with `Content-Type: application/json` and the HTTP status set to match. Prefer it over throwing when the error is part of the route's normal control flow. **Thrown `MochiHttpError`** — same envelope. Use `error(status, message)` from `mochi-framework` to throw one from deep inside helper code where returning is awkward: ```ts import { error } from 'mochi-framework'; error(404, 'Not found'); // → 404 { "error": { "message": "Not found", "status": 404 } } ``` **Any other uncaught throw** — returned as `500 Internal Server Error` with a generic message. The original error (including stack) is logged server-side via `log.error` with the method and path; it is **not** leaked to the client. Throw `MochiHttpError` (or return `apiError(…)`) explicitly when you want a specific status or message to reach the caller. ```ts Mochi.api(async () => { await db.query('SELECT …'); // throws ConnectionError return Response.json({ ok: true }); }); // → 500 { "error": { "message": "Internal Server Error", "status": 500 } } // (real error + stack logged to stderr) ```