# How the withWorkspace Mixin Simplifies Durable Object Integration

> Simplify Durable Object integration with the withWorkspace mixin. It auto-provisions private Workspace instances, reducing boilerplate and enabling seamless cross-Worker RPC communication.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: deep-dive
- Published: 2026-09-04

---

**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`](https://github.com/cloudflare/computer/blob/main/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:

```typescript
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`](https://github.com/cloudflare/computer/blob/main/with-workspace.ts):

```typescript
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:

```typescript
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`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/client.ts) to retrieve the private Workspace instance:

```typescript
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:

```typescript
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`](https://github.com/cloudflare/computer/blob/main/examples/worker-shell/src/index.ts) file demonstrates production-ready usage:

```typescript
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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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.