Routing (2)·续2
import { Agent, routeAgentRequest } from "agents";
export default {
async fetch(request: Request, env: Env) {
return (
(await routeAgentRequest(request, env, {
// Run before WebSocket connections
onBeforeConnect: async (request) => {
const token = new URL(request.url).searchParams.get("token");
if (!(await verifyToken(token, env))) {
// Return a response to reject the connection
return new Response("Unauthorized", { status: 401 });
}
// Return nothing to allow the connection
},
// Run before HTTP requests
onBeforeRequest: async (request) => {
const auth = request.headers.get("Authorization");
if (!auth || !(await verifyAuth(auth, env))) {
return new Response("Unauthorized", { status: 401 });
}
},
// Optional: prepend a prefix to agent instance names
prefix: "user-",
})) ?? new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;
Manual authentication
Check authentication before calling routeAgentRequest():
export default {
async fetch(request, env) {
const url = new URL(request.url);
// Protect agent routes
if (url.pathname.startsWith("/agents/")) {
const user = await authenticate(request, env);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// Optionally, enforce that users can only access their own agents
const instanceName = url.pathname.split("/")[3];
if (instanceName !== `user-${user.id}`) {
return new Response("Forbidden", { status: 403 });
}
}
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
};
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Protect agent routes
if (url.pathname.startsWith("/agents/")) {
const user = await authenticate(request, env);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// Optionally, enforce that users can only access their own agents
const instanceName = url.pathname.split("/")[3];
if (instanceName !== `user-${user.id}`) {
return new Response("Forbidden", { status: 403 });
}
}
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;
Using a framework (Hono)
If you are using a framework like Hono ↗︎, authenticate in middleware before calling the agent:
import { Agent, getAgentByName } from "agents";
import { Hono } from "hono";
const app = new Hono();
// Authentication middleware
app.use("/agents/*", async (c, next) => {
const token = c.req.header("Authorization")?.replace("Bearer ", "");
if (!token || !(await verifyToken(token, c.env))) {
return c.json({ error: "Unauthorized" }, 401);
}
await next();
});
// Route to a specific agent
app.all("/agents/code-review/:id/*", async (c) => {
const id = c.req.param("id");
const agent = await getAgentByName(c.env.CodeReviewAgent, id);
return agent.fetch(c.req.raw);
});
export default app;
import { Agent, getAgentByName } from "agents";
import { Hono } from "hono";
const app = new Hono<{ Bindings: Env }>();
// Authentication middleware
app.use("/agents/*", async (c, next) => {
const token = c.req.header("Authorization")?.replace("Bearer ", "");
if (!token || !(await verifyToken(token, c.env))) {
return c.json({ error: "Unauthorized" }, 401);
}
await next();
});
// Route to a specific agent
app.all("/agents/code-review/:id/*", async (c) => {
const id = c.req.param("id");
const agent = await getAgentByName(c.env.CodeReviewAgent, id);
return agent.fetch(c.req.raw);
});
export default app;
For WebSocket authentication patterns (tokens in URLs, JWT refresh), refer to Cross-domain authentication.
Troubleshooting
Agent namespace not found
The error message lists available agents. Check:
- Agent class is exported from your entry point.
- Class name in code matches
class_nameinwrangler.jsonc. - URL uses correct kebab-case name.
Request returns 404
- Verify the URL pattern:
/agents/{agent-name}/{instance-name}. - Check that
routeAgentRequest()is called before your 404 handler. - Ensure the response from
routeAgentRequest()is returned (not just called).
WebSocket connection fails
- Do not modify the response from
routeAgentRequest()for WebSocket upgrades. - Ensure CORS is enabled if connecting from a different origin.
- Check browser dev tools for the actual error.
basePath not working
- Ensure your Worker handles the custom path and forwards to the agent.
- Use
getAgentByName()+agent.fetch(request)to forward requests. - The
agentparameter is still required but ignored whenbasePathis set. - Check that the server-side route matches the client's
basePath.
API reference
routeAgentRequest(request, env, options?)
Routes a request to the appropriate agent.
| Parameter | Type | Description |
|---|---|---|
request |
Request |
The incoming request |
env |
Env |
Environment with agent bindings |
options.cors |
boolean \| HeadersInit |
Enable CORS headers |
options.props |
Record<string, unknown> |
Props passed to whichever agent handles request |
options.locationHint |
string |
Preferred location for agent instances |
options.jurisdiction |
string |
Data jurisdiction for agent instances |
options.onBeforeConnect |
Function |
Callback before WebSocket connections |
options.onBeforeRequest |
Function |
Callback before HTTP requests |
Returns: Promise<Response | undefined> - Response if matched, undefined if no agent route.
getAgentByName(namespace, name, options?)
Get an agent instance by name for server-side RPC or request forwarding.
| Parameter | Type | Description |
|---|---|---|
namespace |
DurableObjectNamespace<T> |
Agent binding from env |
name |
string |
Instance name |
options.locationHint |
string |
Preferred location |
options.jurisdiction |
string |
Data jurisdiction |
options.props |
Record<string, unknown> |
Initialization properties for onStart |
options.routingRetry |
object |
Retry configuration for transient Durable Object routing failures |
Returns: Promise<DurableObjectStub<T>> - Typed stub for calling agent methods or forwarding requests.
useAgent(options) / AgentClient options
Client connection options for custom routing:
| Option | Type | Description |
|---|---|---|
agent |
string |
Agent class name (required) |
name |
string |
Instance name (default: "default") |
basePath |
string |
Full URL path - bypasses agent/name URL construction |
path |
string |
Additional path to append to the URL |
onIdentity |
(name, agent) => void |
Called when server sends identity |
onIdentityChange |
(oldName, newName, oldAgent, newAgent) => void |
Called when identity changes on reconnect |
Return value properties (React hook):
| Property | Type | Description |
|---|---|---|
name |
string |
Current instance name (reactive) |
agent |
string |
Current agent class name (reactive) |
identified |
boolean |
Whether identity has been received (reactive) |
ready |
Promise<void> |
Resolves when identity is received |
Agent.options (server)
Static options for agent configuration:
| Option | Type | Default | Description |
|---|---|---|---|
hibernate |
boolean |
true |
Whether the agent should hibernate when inactive |
sendIdentityOnConnect |
boolean |
true |
Whether to send identity to clients on connect |
hungScheduleTimeoutSeconds |
number |
30 |
Timeout before a running schedule is considered hung |
class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
}
class SecureAgent extends Agent {
static options = { sendIdentityOnConnect: false };
}
Next steps
Client SDK
Connect from browsers with useAgent and AgentClient.
Cross-domain authentication
WebSocket authentication patterns.
Callable methods
RPC from clients over WebSocket.
Configuration
Set up agent bindings in wrangler.jsonc.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/runtime/communication/routing/#page","headline":"Routing","description":"Route HTTP and WebSocket requests to Agents SDK instances using routeAgentRequest() and getAgentByName().","url":"https://developers.cloudflare.com/agents/runtime/communication/routing/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/runtime/communication/routing/og.png?v=4f8fb7776b8e581c","dateModified":"2026-09-15","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Handle real-time WebSocket connections, messages, broadcasts, and lifecycle hooks in the Agents SDK. title: WebSockets image: https://developers.cloudflare.com/agents/runtime/communication/websockets/og.png?v=b59d4260b0dd4fe9
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.
Docs