Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
32 changes: 32 additions & 0 deletions src/content/docs/workers/miniflare/core/compatibility.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
order: 8
title: "📅 Compatibility Dates"
---

- [Compatibility Dates Reference](/workers/platform/compatibility-dates)

## Compatibility Dates

Miniflare uses compatibility dates to opt-into backwards-incompatible changes
from a specific date. If one isn't set, it will default to some time far in the
past.

```js
const mf = new Miniflare({
compatibilityDate: "2021-11-12",
});
```

## Compatibility Flags

Miniflare also lets you opt-in/out of specific changes using compatibility
flags:

```js
const mf = new Miniflare({
compatibilityFlags: [
"formdata_parser_supports_files",
"durable_object_fetch_allows_relative_url",
],
});
```
105 changes: 105 additions & 0 deletions src/content/docs/workers/miniflare/core/fetch.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
order: 0
title: "📨 Fetch Events"
---

- [`FetchEvent` Reference](/workers/runtime-apis/fetch-event)
- [`FetchEvent` Lifecycle](/workers/learning/fetch-event-lifecycle)
- [`addEventListener` Reference](/workers/runtime-apis/add-event-listener)

## HTTP Requests

Whenever an HTTP request is made, a `Request` object is dispatched to your worker, then the generated `Response` is returned. The
`Request` object will include a
[`cf` object](/workers/runtime-apis/request#incomingrequestcfproperties).
Miniflare will log the method, path, status, and the time it took to respond.

If the Worker throws an error whilst generating a response, an error page
containing the stack trace is returned instead. You can use
[🗺 Source Maps](/developing/source-maps) to make these point to your source
files.

## Dispatching Events

When using the API, the `dispatchFetch` function can be used to dispatch `fetch`
events to your Worker. This can be used for testing responses. `dispatchFetch`
has the same API as the regular `fetch` method: it either takes a `Request`
object, or a URL and optional `RequestInit` object:

```js
import { Miniflare, Request } from "miniflare";

const mf = new Miniflare({
modules: true,
script: `
export default {
async fetch(request, env, ctx) {
const body = JSON.stringify({
url: event.request.url,
header: event.request.headers.get("X-Message"),
});
return new Response(body, {
headers: { "Content-Type": "application/json" },
});
})
}
`,
});

let res = await mf.dispatchFetch("http://localhost:8787/");
console.log(await res.json()); // { url: "http://localhost:8787/", header: null }

res = await mf.dispatchFetch("http://localhost:8787/1", {
headers: { "X-Message": "1" },
});
console.log(await res.json()); // { url: "http://localhost:8787/1", header: "1" }

res = await mf.dispatchFetch(
new Request("http://localhost:8787/2", {
headers: { "X-Message": "2" },
}),
);
console.log(await res.json()); // { url: "http://localhost:8787/2", header: "2" }
```

When dispatching events, you are responsible for adding
[`CF-*` headers](https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-)
and the
[`cf` object](/workers/runtime-apis/request#incomingrequestcfproperties).
This lets you control their values for testing:

```js
const res = await mf.dispatchFetch("http://localhost:8787", {
headers: {
"CF-IPCountry": "GB",
},
cf: {
country: "GB",
},
});
```

## Upstream

Miniflare will call each `fetch` listener until a response is returned. If no
response is returned, or an exception is thrown and `passThroughOnException()`
has been called, the response will be fetched from the specified upstream
instead:

```js
import { Miniflare } from "miniflare";

const mf = new Miniflare({
script: `
addEventListener("fetch", (event) => {
event.passThroughOnException();
throw new Error();
});
`,
upstream: "https://miniflare.dev",
});
// If you don't use the same upstream URL when dispatching, Miniflare will
// rewrite it to match the upstream
const res = await mf.dispatchFetch("https://miniflare.dev/core/fetch");
console.log(await res.text()); // Source code of this page
```
8 changes: 8 additions & 0 deletions src/content/docs/workers/miniflare/core/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Core
order: 2
---

import { DirectoryListing } from "~/components";

<DirectoryListing path="/core" />
69 changes: 69 additions & 0 deletions src/content/docs/workers/miniflare/core/modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
order: 3
title: "📚 Modules"
---

- [Modules Reference](/workers/cli-wrangler/configuration#modules)

## Enabling Modules

Miniflare supports both the traditional `service-worker` and the newer `modules` formats for writing workers. To use the `modules` format, enable it with:

```js
const mf = new Miniflare({
modules: true,
});
```

You can then use `modules` worker scripts like the following:

```js
export default {
async fetch(request, env, ctx) {
// - `request` is the incoming `Request` instance
// - `env` contains bindings, KV namespaces, Durable Objects, etc
// - `ctx` contains `waitUntil` and `passThroughOnException` methods
return new Response("Hello Miniflare!");
},
async scheduled(controller, env, ctx) {
// - `controller` contains `scheduledTime` and `cron` properties
// - `env` contains bindings, KV namespaces, Durable Objects, etc
// - `ctx` contains the `waitUntil` method
console.log("Doing something scheduled...");
},
};
```

<Aside type="warning" header="Warning">

String scripts via the `script` option are supported using
the `modules` format, but you cannot import other modules using them. You must
use a script file via the `scriptPath` option for this.

</Aside>

## Module Rules

Miniflare supports all module types: `ESModule`, `CommonJS`, `Text`, `Data` and
`CompiledWasm`. You can specify additional module resolution rules as follows:

```js
const mf = new Miniflare({
modulesRules: [
{ type: "ESModule", include: ["**/*.js"], fallthrough: true },
{ type: "Text", include: ["**/*.txt"] },
],
});
```

### Default Rules

The following rules are automatically added to the end of your modules rules
list. You can override them by specifying rules matching the same `globs`:

```js
[
{ type: "ESModule", include: ["**/*.mjs"] },
{ type: "CommonJS", include: ["**/*.js", "**/*.cjs"] },
];
```
122 changes: 122 additions & 0 deletions src/content/docs/workers/miniflare/core/multiple-workers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
order: 9
title: "🔌 Multiple Workers"
---

Miniflare allows you to run multiple workers in the same instance. All Workers can be defined at the same level, using the `workers` option.

Here's an example that uses a service binding to increment a value in a shared KV namespace:

```js
import { Miniflare, Response } from "miniflare";

const message = "The count is ";
const mf = new Miniflare({
// Options shared between workers such as HTTP and persistence configuration
// should always be defined at the top level.
host: "0.0.0.0",
port: 8787,
kvPersist: true,

workers: [
{
name: "worker",
kvNamespaces: { COUNTS: "counts" },
serviceBindings: {
INCREMENTER: "incrementer",
// Service bindings can also be defined as custom functions, with access
// to anything defined outside Miniflare.
async CUSTOM(request) {
// `request` is the incoming `Request` object.
return new Response(message);
},
},
modules: true,
script: `export default {
async fetch(request, env, ctx) {
// Get the message defined outside
const response = await env.CUSTOM.fetch("http://host/");
const message = await response.text();

// Increment the count 3 times
await env.INCREMENTER.fetch("http://host/");
await env.INCREMENTER.fetch("http://host/");
await env.INCREMENTER.fetch("http://host/");
const count = await env.COUNTS.get("count");

return new Response(message + count);
}
}`,
},
{
name: "incrementer",
// Note we're using the same `COUNTS` namespace as before, but binding it
// to `NUMBERS` instead.
kvNamespaces: { NUMBERS: "counts" },
// Worker formats can be mixed-and-matched
script: `addEventListener("fetch", (event) => {
event.respondWith(handleRequest());
})
async function handleRequest() {
const count = parseInt((await NUMBERS.get("count")) ?? "0") + 1;
await NUMBERS.put("count", count.toString());
return new Response(count.toString());
}`,
},
],
});
const res = await mf.dispatchFetch("http://localhost");
console.log(await res.text()); // "The count is 3"
await mf.dispose();
```

## Routing

You can enable routing by specifying `routes` via the API,
using the
[standard route syntax](/workers/configuration/routing/routes/#matching-behavior).
Note port numbers are ignored:

```js
const mf = new Miniflare({
workers: [
{
scriptPath: "./api/worker.js",
routes: ["http://127.0.0.1/api*", "api.mf/*"],
},
],
});
```

When using 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:

```sh
# 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.

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

## Durable Objects

Miniflare supports the `script_name` option for accessing Durable Objects
exported by other scripts. See
[📌 Durable Objects](/workers/miniflare/storage/durable-objects#using-a-class-exported-by-another-script)
for more details.
Loading
Loading