How the withWorkspace Mixin Simplifies Durable Object Integration

The withWorkspace mixin is a TypeScript utility that automatically provisions a private Workspace instance for Cloudflare Durable Objects, eliminating boilerplate initialization code while exposing RPC-compatible stubs for seamless cross-Worker communication.

The cloudflare/computer repository provides this factory function to bridge the gap between Durable Objects (DOs) and the Computer platform's Workspace abstraction. Instead of manually wiring storage, session management, and RPC handlers, developers apply the mixin to inherit fully-functional Workspace integration through three automated mechanisms.

What Is the withWorkspace Mixin?

withWorkspace is a higher-order function that returns a class mixin, allowing any Durable Object base class to inherit Workspace capabilities without modification. It accepts two parameters: a Base class constructor and an options callback function that receives the DO instance (self) to derive configuration from runtime context like ctx.storage or env bindings.

According to the source in packages/computer/src/with-workspace.ts (lines 66-69), the generated constructor automatically instantiates new Workspace(options(self)) immediately after calling super(). This ensures every DO instance possesses a ready-to-use Workspace without requiring explicit constructor logic in the subclass.

Three Core Mechanisms of Integration

Automatic Workspace Instantiation

The mixin intercepts the construction sequence to build the Workspace from user-supplied options before any other initialization occurs. Inside the constructor generated by withWorkspace, the system executes:

const workspace = new Workspace(options(self));

This pattern allows the DO to derive Workspace configuration—such as storage pointing to self.ctx.storage or sessionId from self.ctx.id.toString()—directly from its own execution context. The DO author provides only the options callback; the mixin handles the new operator and dependency injection.

Private Symbol Storage

To prevent serialization issues and keep the Workspace hidden from RPC consumers, the mixin stores the instance using a module-private Symbol defined at lines 33-46 of with-workspace.ts:

const WORKSPACE = Symbol("workspace");

The Workspace is assigned to self[WORKSPACE], making it accessible only to code importing the Symbol. Because Symbol-keyed properties are excluded from structured clone serialization, the Workspace never crosses the RPC wire when the DO is accessed via stub. This prevents accidental leakage of internal state while maintaining type safety within the DO's own methods.

RPC-Compatible Stub Exposition

The mixin adds a __getWorkspaceStub() method to the DO prototype (lines 71-77), enabling external Workers to obtain a Workspace reference through standard RPC calls. When a Worker calls getWorkspace(doStub), the helper internally invokes this RPC method to retrieve a WorkspaceStub instance.

This architecture maintains a clean separation: the DO contains the full Workspace implementation, while Workers receive a limited stub interface for calling Workspace methods remotely.

Implementing withWorkspace in Your Durable Object

To integrate the mixin, extend your Durable Object class with the factory result and provide an options callback:

import { DurableObject } from "cloudflare:workers";
import { withWorkspace } from "@cloudflare/computer";

export class MyDO extends withWorkspace(
  class extends DurableObject<Env> {},
  (self) => ({
    storage: self.ctx.storage,
    sessionId: self.ctx.id.toString(),
    backends: [],
  })
) {}

The self parameter represents the instantiated DO, allowing access to ctx (execution context), env (bindings), and other instance properties. The returned options object configures the Workspace's persistence layer and identity.

Accessing the Workspace from Inside and Outside the DO

In-Process Access via getWorkspace

Inside the Durable Object, import getWorkspace from packages/computer/src/client.ts to retrieve the private Workspace instance:

import { getWorkspace } from "@cloudflare/computer";

export class MyDO extends withWorkspace(/* ... */) {
  async fetch(request: Request) {
    const ws = getWorkspace(this);
    await ws.writeFile("/data.txt", "content");
    return new Response("Written");
  }
}

Because this possesses the Symbol-keyed property installed by the mixin, getWorkspace returns the actual Workspace instance with full method access, avoiding RPC overhead for internal operations.

Cross-Worker Access via RPC

External Workers obtain a stub by passing the DO stub to the same getWorkspace function:

import { getWorkspace } from "@cloudflare/computer";

export default {
  async fetch(request, env) {
    const doStub = env.MyDO.get("unique-id");
    const ws = await getWorkspace(doStub);
    const content = await ws.readFile("/data.txt");
    return new Response(content);
  },
};

Here, getWorkspace detects that doStub is an RPC stub and calls __getWorkspaceStub() remotely, returning a WorkspaceStub that proxies method calls back to the DO's Workspace instance.

Complete Working Example

The examples/worker-shell/src/index.ts file demonstrates production-ready usage:

import { withWorkspace } from "@cloudflare/computer";

export class ContainerExample extends withWorkspace(
  class extends DurableObject<Env> {},
  (self) => ({
    storage: self.ctx.storage,
    sessionId: self.ctx.id.toString(),
    backends: [],
  })
) {
  async fetch(request: Request) {
    // Direct Workspace access without RPC
    const ws = getWorkspace(this);
    // ... business logic
  }
}

This pattern appears also in examples/src/agent.ts with the withWorkspaceContainer variant, which extends the same mixin pattern for container-aware DOs.

Summary

  • Automatic provisioning: The mixin instantiates Workspace during construction using a user-provided options callback, eliminating manual boilerplate in packages/computer/src/with-workspace.ts (lines 66-69).
  • Encapsulated storage: A module-private Symbol (Symbol("workspace")) stores the instance privately, preventing serialization across RPC boundaries while maintaining internal access.
  • Dual access patterns: The getWorkspace helper in client.ts provides direct access inside the DO and RPC-based stub access from external Workers via the __getWorkspaceStub() method (lines 71-77).
  • Zero-plumbing integration: DO authors extend the mixin result and implement only the options callback, requiring no additional constructor arguments, property declarations, or RPC handler definitions.

Frequently Asked Questions

What is the withWorkspace mixin in Cloudflare Computer?

withWorkspace is a TypeScript mixin factory in the cloudflare/computer repository that augments Durable Object classes with a fully-initialized Workspace instance. It automates the creation, storage, and RPC exposure of Workspaces, allowing developers to focus on business logic rather than integration plumbing.

How does getWorkspace work across RPC boundaries?

When passed a Durable Object instance, getWorkspace (defined in packages/computer/src/client.ts) retrieves the Symbol-private Workspace property directly. When passed an RPC stub, it asynchronously calls the __getWorkspaceStub() method that withWorkspace installs on the DO prototype (lines 71-77), returning a WorkspaceStub that proxies calls to the actual Workspace residing in the DO.

Why use a Symbol for Workspace storage?

The mixin uses Symbol("workspace") as the property key to guarantee that the Workspace reference is never accidentally cloned during RPC communication or included in JSON serialization. Symbol-keyed properties are non-enumerable and ignored by structured clone algorithms, ensuring the Workspace remains an internal implementation detail invisible to external Workers interacting with the DO stub.

Can I use withWorkspace with custom Durable Object base classes?

Yes. The withWorkspace function accepts any base class extending DurableObject as its first argument. You can pass a custom subclass containing your own methods and properties; the mixin returns a new class that inherits from your base while injecting Workspace functionality through the constructor chain.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →