Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,126 @@ sidebar:
order: 3

---
import { Render, PackageManagers, WranglerConfig } from "~/components";

After you have created a dispatch namespace, you can fetch any user Workers in the namespace using a dynamic dispatch Worker. The dynamic dispatch Worker has a namespace binding.
A [dynamic dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/reference/how-workers-for-platforms-works/#dynamic-dispatch-worker) is a specialized routing Worker that directs incoming requests to the appropriate user Workers in your dispatch namespace. Instead of using [Workers Routes](/workers/configuration/routing/routes/), dispatch Workers let you programmatically control request routing through code.

Use any method of routing to a namespaced Worker (reading the subdomain, request header, or lookup in a database). Ultimately you need the name of the user Worker.

In the following example, routing to a user Worker is done through reading the subdomain `<USER_WORKER_NAME>.example.com/*`. For example, `my-customer.example.com` will run the script uploaded to `PUT accounts/<ACCOUNT_ID>/workers/dispatch/namespaces/my-dispatch-namespace/scripts/my-customer`.
![Figure 1: Workers for Platforms: Main Flow](~/assets/images/reference-architecture/programmable-platforms/programmable-platforms-1.svg)


#### Why use a dynamic dispatch Worker?

* **Scale**: Perfect for routing thousands or millions of hostnames to different Workers, without needing to rely on [Workers Routes](/workers/configuration/routing/routes/)
* **Custom routing logic**: Write code to determine exactly how requests should be routed. For example:
* Store hostname-to-Worker mappings in [Workers KV](/kv/) and look them up dynamically
* Route requests based on subdomain, path, headers, or other request properties
* Use [custom metadata](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/) attached to [custom hostnames](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/) for routing decisions

### Configure the dispatch namespace binding
To allow your dynamic dispatch Worker to dynamically route requests to Workers in a namespace, you need to configure a dispatch namespace [binding](/workers/runtime-apis/bindings/). This binding enables your dynamic dispatch Worker to call any user Worker within that namespace using `env.dispatcher.get()`.

<WranglerConfig>

```toml
[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-dispatch-namespace"
```

</WranglerConfig>

Once the binding is configured, your dynamic dispatch Worker can route requests to any Worker in the namespace. Below are common routing patterns you can implement in your dispatcher.

### Routing examples

![Figure 2: Workers for Platforms: Main Flow](~/assets/images/reference-architecture/programmable-platforms/programmable-platforms-2.svg)


#### KV-Based Routing
Store the routing mappings in [Workers KV](/kv/). This allows you to modify your routing logic without requiring you to change or redeploy the dynamic dispatch Worker.

```js
export default {
async fetch(request, env) {
try {
const url = new URL(request.url);

// Use hostname, path, or any combination as the routing key
const routingKey = url.hostname;

// Lookup user Worker name from KV store
const userWorkerName = await env.USER_ROUTING.get(routingKey);

if (!userWorkerName) {
return new Response('Route not configured', { status: 404 });
}

// Optional: Cache the KV lookup result
const userWorker = env.DISPATCHER.get(userWorkerName);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
return new Response('', { status: 404 });
}
return new Response(e.message, { status: 500 });
}
}
};
```


#### Subdomain-Based Routing
Route subdomains to the corresponding Worker. For example, `my-customer.example.com` will route to the Worker named `my-customer` in the dispatch namespace.

```js
export default {
async fetch(request, env) {
try {
// parse the URL, read the subdomain
let workerName = new URL(request.url).host.split('.')[0];
let userWorker = env.dispatcher.get(workerName);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
// we tried to get a worker that doesn't exist in our dispatch namespace
return new Response('', { status: 404 });
}

// this could be any other exception from `fetch()` *or* an exception
// thrown by the called worker (e.g. if the dispatched worker has
// `throw MyException()`, you could check for that here).
return new Response(e.message, { status: 500 });
}
},
async fetch(request, env) {
try {
// Extract user Worker name from subdomain
// Example: customer1.example.com -> customer1
const url = new URL(request.url);
const userWorkerName = url.hostname.split('.')[0];

// Get user Worker from dispatch namespace
const userWorker = env.DISPATCHER.get(userWorkerName);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
// User Worker doesn't exist in dispatch namespace
return new Response('', { status: 404 });
}
// Could be any other exception from fetch() or from the dispatched Worker
return new Response(e.message, { status: 500 });
}
}
};
```
#### Path-Based routing
Route URL paths to the corresponding Worker. For example, `example.com/customer-1` will route to the Worker named `customer-1` in the dispatch namespace.

```js
export default {
async fetch(request, env) {
try {
const url = new URL(request.url);
const pathParts = url.pathname.split('/').filter(Boolean);

if (pathParts.length === 0) {
return new Response('Invalid path', { status: 400 });
}

// example.com/customer-1 -> routes to 'customer-1' worker
const userWorkerName = pathParts[0];

const userWorker = env.DISPATCHER.get(userWorkerName);
return await userWorker.fetch(request);
} catch (e) {
if (e.message.startsWith('Worker not found')) {
return new Response('', { status: 404 });
}
return new Response(e.message, { status: 500 });
}
}
};
```