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
36 changes: 36 additions & 0 deletions .changeset/chatty-doors-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
"wrangler": minor
---

Enable using `ctx.exports` with containers

You can now use containers with Durable Objects that are accessed via [`ctx.exports`](https://developers.cloudflare.com/workers/runtime-apis/context/#exports).

Now your config file can look something like this:

```
{
"name": "container-app",
"main": "src/index.ts",
"compatibility_date": "2025-12-01",
"compatibility_flags": ["enable_ctx_exports"], // compat flag needed for now.
"containers": [
{
"image": "./Dockerfile",
"class_name": "MyDOClassname",
"name": "my-container"
},
],
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyDOClassname"],
},
],
// no need to declare your durable object binding here
}
```

Note that when using `ctx.exports`, where you previously accessed a Durable Object via something like `env.DO`, you should now access with `ctx.exports.MyDOClassname`.

Refer to [the docs for more information on using `ctx.exports`](https://developers.cloudflare.com/workers/runtime-apis/context/#exports).
11 changes: 6 additions & 5 deletions fixtures/container-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,18 @@ export class FixtureTestContainer extends DurableObject<Env> {
}

export default {
async fetch(request, env): Promise<Response> {
async fetch(request, env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/second") {
// This is a second Durable Object that can be used to test multiple DOs
const id = env.CONTAINER.idFromName("second-container");
const stub = env.CONTAINER.get(id);
const id =
ctx.exports.FixtureTestContainer.idFromName("second-container");
const stub = ctx.exports.FixtureTestContainer.get(id);
const query = url.searchParams.get("req");
return stub.fetch("http://example.com/" + query);
}
const id = env.CONTAINER.idFromName("container");
const stub = env.CONTAINER.get(id);
const id = ctx.exports.FixtureTestContainer.idFromName("container");
const stub = ctx.exports.FixtureTestContainer.get(id);
return stub.fetch(request);
},
} satisfies ExportedHandler<Env>;
9 changes: 1 addition & 8 deletions fixtures/container-app/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "container-app",
"main": "src/index.ts",
"compatibility_date": "2025-04-03",
"compatibility_flags": ["enable_ctx_exports"],
"containers": [
{
"image": "./Dockerfile",
Expand All @@ -10,14 +11,6 @@
"max_instances": 2,
},
],
"durable_objects": {
"bindings": [
{
"class_name": "FixtureTestContainer",
"name": "CONTAINER",
},
],
},
"migrations": [
{
"tag": "v1",
Expand Down
4 changes: 2 additions & 2 deletions packages/wrangler/e2e/dev-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ describe.each([{ cmd: "wrangler dev" }])("dev registry $cmd", ({ cmd }) => {
);

expect(normalizeOutput(workerA.currentOutput)).toContain(
"connect to other wrangler or vite dev processes running locally"
"connect to other Wrangler or Vite dev processes running locally"
);
});

Expand Down Expand Up @@ -595,7 +595,7 @@ describe.each([{ cmd: "wrangler dev" }])("dev registry $cmd", ({ cmd }) => {
);

expect(normalizeOutput(workerA.currentOutput)).toContain(
"connect to other wrangler or vite dev processes running locally"
"connect to other Wrangler or Vite dev processes running locally"
);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/wrangler/e2e/pages-dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ describe.sequential("wrangler pages dev", () => {
env.VAR1 ("(hidden)") Environment Variable local
env.VAR2 ("VAR_2_TOML") Environment Variable local
env.VAR3 ("(hidden)") Environment Variable local
Service bindings, Durable Object bindings, and Tail consumers connect to other wrangler or vite dev processes running locally, with their connection status indicated by [connected] or [not connected]. For more details, refer to https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/#local-development
Service bindings, Durable Object bindings, and Tail consumers connect to other Wrangler or Vite dev processes running locally, with their connection status indicated by [connected] or [not connected]. For more details, refer to https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/#local-development
"
`);
});
Expand Down
19 changes: 19 additions & 0 deletions packages/wrangler/src/__tests__/containers/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

await expect(getNormalizedContainerOptions(config, {})).rejects.toThrow(
Expand Down Expand Up @@ -132,6 +133,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -176,6 +178,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -219,6 +222,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -260,6 +264,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -307,6 +312,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -351,6 +357,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -393,6 +400,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -448,6 +456,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -500,6 +509,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -552,6 +562,9 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [
{ tag: "v1", new_sqlite_classes: ["Container1", "Container2"] },
],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -583,6 +596,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -615,6 +629,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -644,6 +659,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;

const result = await getNormalizedContainerOptions(config, {});
Expand Down Expand Up @@ -675,6 +691,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;
await expect(getNormalizedContainerOptions(config, {})).rejects
.toThrowErrorMatchingInlineSnapshot(`
Expand Down Expand Up @@ -706,6 +723,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;
const result = await getNormalizedContainerOptions(config, {});
expect(result).toHaveLength(1);
Expand Down Expand Up @@ -737,6 +755,7 @@ describe("getNormalizedContainerOptions", () => {
},
],
},
migrations: [{ tag: "v1", new_sqlite_classes: ["TestContainer"] }],
} as Partial<Config> as Config;
const result = await getNormalizedContainerOptions(config, {
dryRun: true,
Expand Down
Loading
Loading