Runtime Server Adapter
Deploy the CopilotKit runtime on any backend framework — Node.js, Express, Hono, Bun, Deno, Cloudflare Workers, and more.
The CopilotKit runtime is framework-agnostic. At its core, it's a pure Fetch handler — a function that takes a Request and returns a Response. This means it runs natively on any platform that supports the Fetch API, and thin adapters are provided for Node.js-based frameworks like Express and Hono.
Quick Overview#
| Runtime | Import Path | Key Function |
|---|---|---|
| Fetch-native (Bun, Deno, CF Workers, Next.js App Router) | @copilotkit/runtime/v2 | createCopilotRuntimeHandler |
| Node.js HTTP | @copilotkit/runtime/v2/node | createCopilotNodeHandler / createCopilotNodeListener |
| Express | @copilotkit/runtime/v2/express | createCopilotExpressHandler |
| Hono | @copilotkit/runtime/v2/hono | createCopilotHonoHandler |
Multi-Route vs Single-Route#
The runtime supports two endpoint modes:
-
Multi-route (default) — exposes individual URL endpoints:
GET {basePath}/infoPOST {basePath}/agent/:agentId/runPOST {basePath}/agent/:agentId/connectPOST {basePath}/agent/:agentId/stop/:threadIdPOST {basePath}/transcribe
-
Single-route — a single
POST {basePath}endpoint that accepts a JSON envelope:{ "method": "agent/run", "params": { "agentId": "default" }, "body": { ... } }
Single-route mode is useful when your platform only allows a single route handler (e.g. a single serverless function), or when you prefer a simpler URL structure.
Fetch-Native Runtimes#
If your platform supports the Fetch API natively (Bun, Deno, Cloudflare Workers, etc.), you can use createCopilotRuntimeHandler directly — no adapter needed.
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";
// 1. Create the runtime with your agent(s)
const runtime = new CopilotRuntime({
agents: {
default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
},
});
// 2. Create a Fetch handler — takes Request, returns Response
const handler = createCopilotRuntimeHandler({
runtime,
basePath: "/api/copilotkit",
cors: true,
});
// 3. Serve it (Bun example — works the same with Deno.serve, CF Workers, etc.)
Bun.serve({ fetch: handler, port: 4000 });
For framework-specific examples, see the sections below.
Bun#
Deno#
Cloudflare Workers#
Next.js App Router#
No cors: true needed for Next.js — same-origin requests don't require CORS headers.
React Router (Framework Mode)#
React Router v7 in framework mode uses file-based routing with Fetch API handlers — the CopilotKit handler works directly as a resource route.
TanStack Start#
TanStack Start uses API routes that receive standard Request objects and return Response objects.
Node.js HTTP#
For vanilla Node.js HTTP servers, use the /node subpath which bridges Fetch to Node's IncomingMessage/ServerResponse.
Express#
The Express adapter returns an Express Router that you mount with app.use().
npm install express cors
Express Options#
| Option | Type | Default | Description |
|---|---|---|---|
runtime | CopilotRuntime | required | The runtime instance |
basePath | string | required | URL path prefix (e.g. "/api/copilotkit") |
mode | "multi-route" | "single-route" | "multi-route" | Multi-route exposes individual endpoints; single-route uses a JSON envelope |
cors | boolean | CorsOptions | false | CORS configuration. true for permissive defaults, or pass a cors options object |
hooks | CopilotRuntimeHooks | — | Lifecycle hooks |
The Express adapter is compatible with express.json() body parsing. If you have app.use(express.json()) before the CopilotKit router, the adapter will detect the pre-parsed body and handle it correctly.
Hono#
The Hono adapter returns a Hono app that you mount with app.route().
npm install hono
Elysia (Bun)#
Elysia runs on Bun and supports the Fetch API natively, so you use createCopilotRuntimeHandler directly.
bun add elysia
Lifecycle Hooks#
All adapters support lifecycle hooks for cross-cutting concerns like authentication, logging, and response modification.
const handler = createCopilotRuntimeHandler({
runtime,
basePath: "/api/copilotkit",
hooks: {
// Before routing — auth, correlation IDs
onRequest: async ({ request, path, runtime }) => {
const token = request.headers.get("authorization");
if (!token) {
throw new Response("Unauthorized", { status: 401 });
}
},
// After routing — route-specific authorization
onBeforeHandler: async ({ request, route }) => {
console.log(`Handling ${route.method} for agent ${route.agentId}`);
},
// After handler — modify response headers
onResponse: async ({ response, request }) => {
const headers = new Headers(response.headers);
headers.set("x-request-id", crypto.randomUUID());
return new Response(response.body, {
status: response.status,
headers,
});
},
// On error — custom error responses
onError: async ({ error, request }) => {
console.error("Handler error:", error);
},
},
});
| Hook | When | Can modify |
|---|---|---|
onRequest | Before routing | Throw Response to short-circuit |
onBeforeHandler | After routing, before handler | Access route info (method, agentId, threadId) |
onResponse | After handler | Return a new Response to replace it |
onError | On unhandled error | Log or produce a custom error response |
CORS Configuration#
Fetch-native runtimes#
Pass cors: true for permissive defaults (Access-Control-Allow-Origin: *), or provide a config object:
const handler = createCopilotRuntimeHandler({
runtime,
basePath: "/api/copilotkit",
cors: {
origin: "https://myapp.com",
credentials: true,
},
});
Express#
Pass cors: true for permissive defaults, or pass a cors options object:
createCopilotExpressHandler({
runtime,
basePath: "/api/copilotkit",
cors: {
origin: "https://myapp.com",
credentials: true,
},
});
Hono#
Pass a cors config with explicit origin:
createCopilotHonoHandler({
runtime,
basePath: "/api/copilotkit",
cors: {
origin: "https://myapp.com",
credentials: true,
},
});
When using credentials: true, you must specify an explicit origin — wildcard (*) is not allowed by the CORS spec.
Connecting Your Frontend#
Once your runtime is running, point your frontend at it:
<CopilotKit runtimeUrl="http://localhost:4000/api/copilotkit">
<YourApp />
</CopilotKit>
For same-origin deployments (e.g. Next.js, React Router, TanStack Start), use a relative path:
<CopilotKit runtimeUrl="/api/copilotkit">
<YourApp />
</CopilotKit>
