Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
45 changes: 45 additions & 0 deletions samples/multiple-codebases/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Multiple Codebases Example

This example runs two applications with separate Worker scripts in one workerd
process. Both applications share a listening socket. A router Worker selects an
application by the request URL's hostname and forwards the request through a
service binding. Unrecognized hostnames receive a 404 response.

Use this pattern for codebases you trust, such as multiple applications belonging
to the same organization. Host-based routing does not provide the security
isolation needed to run arbitrary, potentially malicious tenant code.

## Run

From this directory, using a workerd binary:

```sh
workerd serve config.capnp
```

Or from the repository root, using Bazel:

```sh
bazel run //src/workerd/server:workerd -- serve "$PWD/samples/multiple-codebases/config.capnp"
```

## Try Both Applications

The example listens on `127.0.0.1:8080`. Set the `Host` header to select an
application without configuring local DNS:

```sh
curl -H 'Host: one.example.test' 'http://127.0.0.1:8080/hello?name=one'
# App one: http://one.example.test/hello?name=one

curl -H 'Host: two.example.test' 'http://127.0.0.1:8080/hello?name=two'
# App two: http://two.example.test/hello?name=two

curl -i -H 'Host: unknown.example.test' http://127.0.0.1:8080/
# HTTP/1.1 404 Not Found
```

Each application's service can have its own modules, bindings, and compatibility
date. To add an application, configure its service, give the router a binding to
that service, and add its hostname to the router. The original request, including
its path, query string, method, and body, is passed to the selected application.
9 changes: 9 additions & 0 deletions samples/multiple-codebases/app-one.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2026 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0

export default {
fetch(request) {
return new Response(`App one: ${request.url}\n`);
},
};
9 changes: 9 additions & 0 deletions samples/multiple-codebases/app-two.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2026 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0

export default {
fetch(request) {
return new Response(`App two: ${request.url}\n`);
},
};
25 changes: 25 additions & 0 deletions samples/multiple-codebases/config.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Workerd = import "/workerd/workerd.capnp";

const multipleCodebasesExample :Workerd.Config = (
services = [
(name = "router", worker = (
modules = [(name = "router", esModule = embed "router.js")],
compatibilityDate = "2023-02-28",
bindings = [
(name = "APP_ONE", service = "app-one"),
(name = "APP_TWO", service = "app-two"),
],
)),
(name = "app-one", worker = (
modules = [(name = "app", esModule = embed "app-one.js")],
compatibilityDate = "2023-02-28",
)),
(name = "app-two", worker = (
modules = [(name = "app", esModule = embed "app-two.js")],
compatibilityDate = "2023-02-28",
)),
],
sockets = [
(name = "http", address = "127.0.0.1:8080", http = (), service = "router"),
],
);
16 changes: 16 additions & 0 deletions samples/multiple-codebases/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2026 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0

export default {
fetch(request, env) {
switch (new URL(request.url).hostname) {
case 'one.example.test':
return env.APP_ONE.fetch(request);
case 'two.example.test':
return env.APP_TWO.fetch(request);
default:
return new Response('Unknown host\n', { status: 404 });
}
},
};
Loading