Skip to content
Miniflare
Visit Miniflare on GitHub
Set theme to dark (โ‡ง+D)

๐Ÿ”Œ Multiple Workers

Mounting Workers

Miniflare allows you to run multiple workers in the same instance. Assuming the following directory structure:

โ”œโ”€โ”€ api
โ”‚ย ย  โ”œโ”€โ”€ api-worker.js // addEventListener("fetch", ...)
โ”‚ย ย  โ”œโ”€โ”€ package.json // { "main": "./api-worker.js" }
โ”‚ย ย  โ””โ”€โ”€ wrangler.toml // name = "api"
โ”œโ”€โ”€ site
โ”‚ย ย  โ”œโ”€โ”€ package.json // { "module": "./site-worker.mjs" }
โ”‚ย ย  โ”œโ”€โ”€ site-worker.mjs // export default { ... }
โ”‚ย ย  โ””โ”€โ”€ wrangler.toml // name = "site" [build.upload] format = "modules"
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ worker.js
โ””โ”€โ”€ wrangler.toml

...you can mount the api and site workers (using the dev environment in site's wrangler.toml) with:

$ miniflare --mount api=./api --mount site=./site@dev
wrangler.toml
# Paths resolved relative to wrangler.toml's directory
[miniflare.mounts]
api = "./api"
site = "./site@dev"
const mf = new Miniflare({
mounts: {
api: "./api",
site: {
rootPath: "./site",
wranglerConfigPath: true,
wranglerConfigEnv: "dev",
packagePath: true,
envPath: true,
},
},
});

Note the mounted paths, ./api and ./site, are paths to directories not worker scripts. All worker configuration must be derivable from package.json, .env and wrangler.toml files in these directories when mounting like this. None of the configuration from the parent worker (aside from the watch option) is copied to mounted workers.

When using the API, you can instead configure the mounted workers using the same options as the new Miniflare constructor. Note that nested mounts are not supported, but all mounts are automatically accessible to all other mounts (e.g. for use in Durable Object bindings).

const mf = new Miniflare({
mounts: {
api: {
rootPath: "./api",
scriptPath: "./api-worker.js",
kvNamespaces: ["TEST_NAMESPACE"],
},
},
});

Routing

By default, mounted workers are not accessible. You can enable routing by specifying routes in the mounted worker's wrangler.toml file or via the API, using the standard route syntax. Note port numbers are ignored:

api/wrangler.toml
# Miniflare will load routes from any of these options
route = "https://example.com/api/*"
routes = ["example.com/v1/*", "example.com/v2/*"]
# Miniflare supports Wrangler2 routes. Zones are ignored
route = {pattern = "https://example.com/api/*", zone_name="<ignored>"}
routes = [{pattern = "example.com/v1/*", zone_name="<ignored>"}, {pattern = "example.com/v2/*", zone_id = "<ignored>"}]
# Only loaded if the wrangler.toml environment is set to "dev"
[env.dev]
route = "miniflare.test/api/*"
routes = ["miniflare.test/v1/*", "miniflare.test/v2/*"]
# Only loaded by Miniflare, ignored when deploying
[miniflare]
route = "http://127.0.0.1/api*"
routes = ["api.mf/*"]
const mf = new Miniflare({
mounts: {
api: {
rootPath: "./api",
wranglerConfigPath: true,
packagePath: true,
envPath: true,
routes: ["http://127.0.0.1/api*", "api.mf/*"],
},
},
});

The parent worker is always used as a fallback if no mounts' routes match. If the parent worker has a name set, and it has more specific routes than other mounts, they'll be used instead.

$ miniflare --name worker --route http://127.0.0.1/parent*
wrangler.toml
name = "worker"
route = "http://127.0.0.1/parent*"
const mf = new Miniflare({
name: "worker",
routes: ["http://127.0.0.1/parent*"],
});

When using the CLI with hostnames that aren't localhost or 127.0.0.1, you may need to edit your computer's hosts file, so those hostnames resolve to localhost. On Linux and macOS, this is usually at /etc/hosts. On Windows, it's at C:\Windows\System32\drivers\etc\hosts. For the routes above, we would need to append the following entries to the file:

127.0.0.1 miniflare.test
127.0.0.1 api.mf

Alternatively, you can customise the Host header when sending the request:

# Dispatches to the "api" worker
$ curl "http://localhost:8787/todos/update/1" -H "Host: api.mf"

When using the API, Miniflare will use the request's URL to determine which worker to dispatch to.

// Dispatches to the "api" worker
const res = await mf.dispatchFetch("http://api.mf/todos/update/1", { ... });

Note that if an upstream is specified, Miniflare will use the incoming request's URL for route matching, but then replace it and the Host header with the upstream:

const mf = new Miniflare({
mounts: {
api: {
script: `export default {
async fetch(request) {
return new Response("URL: " + request.url + " Host: " + request.headers.get("Host"));
}
}`,
modules: true,
upstream: "https://example.com/api/",
routes: ["api.mf/*"],
},
},
});
const res = await mf.dispatchFetch("http://api.mf/todos/update/1", {
headers: { Host: "api.mf" },
});
console.log(await res.text()); // URL: https://example.com/api/todos/update/1 Host: example.com

Scheduled Events HTTP Triggers

โฐ Scheduled Events can be triggered by making HTTP requests to /cdn-cgi/mf/scheduled. These requests respect the same routes as fetch events. For example, requesting http://api.mf/cdn-cgi/mf/scheduled will trigger a scheduled event in the api worker if its routes include api.mf/*.

Durable Objects

Miniflare supports the script_name option for accessing Durable Objects exported by other scripts. See ๐Ÿ“Œ Durable Objects for more details.