Quickback Docs

Realtime

Real-time updates via WebSockets with Cloudflare Durable Objects.

Quickback ships two parallel realtime primitives, both backed by Cloudflare Durable Objects:

  • /broadcast/v1/* — server fan-out. One DO instance per subscription scope. Postgres-changes events from CRUD routes plus custom broadcast events flow out to every subscriber, with role filtering and per-role field masking applied. Best for "tell every client in this shared scope that something changed." The scope can be an organization, a specific user, or a compiler-resolved resource key like events:evt_123. Documented on this page.
  • /realtime/<binding>/<room-id> — bidirectional rooms. One DO instance per room (per document, per interview, per game). Client↔client messages, presence, and CRDT-friendly state. Use these for collaborative editing. See PartyServer rooms.

Both share the same auth model and ride on the same compiled worker; they solve different problems.

Architecture

┌──────────────────────────────────────────┐
│            Quickback Worker              │
│                                          │
│  ┌──────────┐    DO binding   ┌────────────────────┐
│  │ Hono API │ ──────────────► │ Broadcaster (DO)   │
│  │          │                 │ WebSocket manager   │
│  └──────────┘                 └─────────┬──────────┘
│                                         │
└─────────────────────────────────────────┼┘
                                          │ WebSocket

                                 ┌────────▼─────────┐
                                 │  Browser Clients  │
                                 │   (CMS, Account,  │
                                 │    Admin, Custom)  │
                                 └──────────────────┘

The Broadcaster Durable Object runs inline in your main Quickback worker — no separate worker deployment needed. The API calls the DO directly via its binding (in-process, no network hop).

  1. Quickback Worker — Your compiled API with the Broadcaster DO class exported from the same worker.
  2. Broadcaster (Durable Object) — Manages WebSocket connections per subscription scope. One instance per scopeKey, organization, or user lane.
  3. Browser Clients — CMS, Account, Admin, and custom frontends connect via WebSocket on the same origin.

Key Features

FeatureDescription
Scope-aware fan-outRoutes by scopeKey, organizationId, or userId
Role-based filteringOnly send events to users with matching roles
Per-role maskingDifferent users see different field values based on their role
User-specific targetingSend events to a specific user within an org
Resource-scoped ticketsReuse authz relationship roles to mint scope-bound ws tickets
Custom broadcastsArbitrary events beyond CRUD
Custom namespacesdefineRealtime() for type-safe event helpers
Ticket-based authHMAC-signed tickets verified at WebSocket upgrade — no HTTP round-trip

Enabling Realtime

Add realtime to individual table definitions:

export default defineTable(applications, {
  firewall: [{ field: 'organizationId', equals: 'ctx.activeOrgId' }],
  realtime: {
    enabled: true,
    onInsert: true,
    onUpdate: true,
    onDelete: true,
    requiredRoles: ["recruiter", "hiring-manager"],
    fields: ["id", "candidateId", "stage"],
  },
});

And enable the realtime binding in your database config. The compiler generates the Durable Object class, helper functions, and wrangler bindings — all within your main worker.

providers: {
  database: defineDatabase("cloudflare-d1", {
    realtime: {
      wsTicket: {
        role: "attendee",
        requestField: "eventId",
        scopeTable: "events",
      },
    },
  }),
}

With that config, /broadcast/v1/ws-ticket can authenticate the caller, verify the declared authz relationship role against body.eventId, and mint a short-lived ticket scoped to the resolved events:<id> channel.

Pages

  • Durable Objects Setup — Broadcaster configuration, wrangler bindings, event formats, masking, and custom namespaces
  • Using Realtime — Subscribing to /broadcast/v1/*: WebSocket connection, ticket auth, client-side handling
  • PartyServer Rooms — Per-room collab via /realtime/<binding>/<room-id>: presence, live notes, room IDs

On this page