API routes let you create HTTP endpoints in your One app. Add +api.ts to the end of your filename and export handler functions for each HTTP method.
app/api/hello+api.ts
This creates an endpoint at /api/hello that responds to GET requests.
Export named functions for each HTTP method:
app/api/users+api.ts
You can also export a default function as a catch-all:
API route handlers — including HEAD and OPTIONS — are dispatched the same way in both one dev (Vite) and one serve (Hono on Node / Cloudflare Workers / Vercel). Your middleware chain and method exports are the source of truth: whatever response they produce in dev is what ships in prod.
For OPTIONS preflight specifically, One configures Vite’s dev server with server.cors: { preflightContinue: true, origin: true, credentials: true } so the built-in cors middleware sets permissive headers on non-preflight requests but lets preflight OPTIONS fall through to your handlers. If you override server.cors in your own vite.config.ts, keep preflightContinue: true or your middleware’s preflight response won’t reach the browser in dev.
Use brackets for dynamic segments. Params are passed as the second argument:
app/api/users/[id]+api.ts
Rest parameters work the same way:
app/api/files/[...path]+api.ts
API routes use the standard Web Request and Response APIs. TypeScript and Node >= 20 include these globally.
One exports an Endpoint type for simple cases:
For typed params, use createAPIRoute:
When deployed to Cloudflare Workers, API handlers also receive a worker context containing the worker’s env bindings and executionCtx (with waitUntil and passThroughOnException). The worker key is undefined on Node/dev so you can write runtime-portable code.
app/api/track+api.ts
The worker context has this shape:
Use env to access bindings you’ve configured in wrangler.json (databases, KV namespaces, secrets, the ASSETS static-asset binding), and executionCtx.waitUntil() for background work that should outlive the response.
Edit this page on GitHub.