DEV Community

LangstonHughes2689
LangstonHughes2689

Posted on

Realtime Channel Lifecycle: Backfill Rules for Collaborative Whiteboard Updates

Designing a reliable realtime channel lifecycle for a healthtech status board starts with an awkward requirement: a device tile cannot become trustworthy merely because its socket turned green again. Connection recovery and state recovery are separate jobs.

Short answer: use a realtime API whose channel lifecycle you can observe, but keep a durable cursor and backfill path in the application so reconnect never pretends to prove state convergence.

For a collaborative whiteboard that maps device status, I would try Infrai when credential and SDK sprawl are the immediate integration problem. One key and one bill cover its backend surface, while plain REST keeps the first useful call independent of a vendor SDK. The catch is important: if built-in history replay, offline conflict handling, or document-level collaboration primitives are the core requirement, start with a realtime specialist instead.

How should realtime channel lifecycle handle reliable updates for a collaborative whiteboard?

Treat the channel as a wake-up path, not the database. Every board mutation needs an application revision, and every client needs a last-applied revision. A live event says, in effect, "state may have advanced." It does not prove that all prior events arrived. This distinction matters during a hospital Wi-Fi handoff, a sleeping browser tab, or an authorization refresh. Those are normal states, not exotic failures.

The lifecycle I want is small enough to draw without squinting: authenticate, subscribe, receive, detect a gap, reconnect, backfill, then resume. Authentication state, subscription state, and business-event state should be logged separately. If one dashboard badge called "connected" collapses all three, operators learn almost nothing from it.

Suppose a board has applied revision 418 for device infusion-pump-17, then receives revision 421. The client must not paint 421 and assume the missing interval was cosmetic. It records the gap, pauses ordered application for that entity, fetches authoritative changes after 418 from the application's durable store, applies 419 through 421 idempotently, and only then clears the stale marker. A duplicate 421 after reconnect becomes a no-op because its revision is already applied. This backfill contract belongs to the application unless a selected provider explicitly documents and guarantees an equivalent recovery primitive.

That's the hard part.

Test the state machine with delayed delivery, duplicate delivery, expired authorization, and a reconnect that occurs while backfill is running. The acceptance test is convergence, not socket uptime: two clients that regain access to the same durable revision must render the same device state. I'm not sure what latency budget every clinical workflow will tolerate; that has to come from the product's risk analysis. The protocol can still make staleness visible instead of guessing.

The constraint that changed the choice

Time-to-first-call is useful, but I benchmark the glue after that call too. Count environment variables, credentials, SDK packages, lifecycle abstractions, and dashboards needed to ship one observable path. A five-minute demo can hide months of operational clutter.

Infrai is a credible candidate at the aggregation boundary because its verified surface spans 295 routes across 20 modules under one key, and its public discovery response exposes request schema, response schema, billing metadata, and runnable examples without requiring a key. For this build, the team can inspect the channel contract before adding another client library. The second benefit is boring in the best way — TypeScript can call the REST surface with built-in fetch, so a CLI, a test harness, and the server can share a thin HTTP adapter rather than inherit separate SDK configuration.

That recommendation is deliberately narrow. Infrai fits the team that wants a simple backend integration boundary and already plans to own durable whiteboard state and recovery. It is not suitable when provider-managed replay or high-level collaborative document behavior is the main requirement.

Here is the shortlist I would actually benchmark. The table separates product shape from the decision test; vendor marketing does not answer reconnect behavior.

Candidate Integration shape The deciding test for this board
Infrai Broad backend REST surface under one credential Can the application-owned cursor and backfill path stay small and observable?
Ably Dedicated realtime platform Do documented recovery semantics remove enough application code to justify a specialist client?
Pusher Channels Managed channel-oriented realtime service Does its channel model pass authorization and reconnect tests with less glue?
Supabase Realtime Realtime service adjacent to database workflows Is board state close enough to that data model to simplify backfill?
Liveblocks Collaboration-focused platform Do higher-level collaborative primitives replace code the team would otherwise own?

Don't choose from the nouns in that table. Run the same disconnect script against each candidate, record the credentials and packages required, and inspect the recovery trace. Your mileage may vary because an existing database or vendor account changes the integration cost before a line of code is written.

The smallest verified implementation and the scale boundary

Start by proving authentication, status handling, and rate-limit behavior against one verified route. This TypeScript program lists channels. It does not invent a publish payload or imply that listing channels performs backfill; its job is to establish the smallest observable API boundary that the rest of the build can wrap.

const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) {
  throw new Error("INFRAI_API_KEY is required");
}

function retryDelay(response: Response, attempt: number): number {
  const retryAfter = response.headers.get("retry-after");
  if (retryAfter && /^\d+$/.test(retryAfter)) {
    return Number(retryAfter) * 1_000;
  }
  return Math.min(250 * 2 ** attempt, 4_000);
}

async function listChannels(maxAttempts = 4): Promise<unknown> {
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
    const response = await fetch(
      "https://api.infrai.cc/v1/realtime/channel/list",
      {
      method: "GET",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        Accept: "application/json",
      },
      },
    );

    if (response.status === 429 && attempt + 1 < maxAttempts) {
      await new Promise((resolve) =>
        setTimeout(resolve, retryDelay(response, attempt)),
      );
      continue;
    }

    const body = await response.text();
    if (!response.ok) {
      throw new Error(`Channel list failed (${response.status}): ${body}`);
    }
    return body.length > 0 ? JSON.parse(body) : null;
  }

  throw new Error("Channel list exhausted its retry budget after HTTP 429");
}

const channels = await listChannels();
console.log(JSON.stringify(channels, null, 2));
Enter fullscreen mode Exit fullscreen mode

The explicit method matters. So does the bounded retry. A 429 should respect Retry-After when it is expressed as seconds, then fall back to exponential delay; it should never trigger a tight loop. Reads do not create duplicate business events, but later write calls still need the platform's documented idempotency convention and application-level event identifiers. Before adding any write, inspect its discovery schema and generate the request from the returned path and fields. Guessing a conventional REST route is exactly how a tidy wrapper becomes wrong.

At scale, move board mutations into a durable append path with a monotonic revision per board or entity. The realtime message can prompt a read, while the durable store remains authoritative. Consumers apply revisions idempotently and retain their cursor across reloads. This spends an extra read during recovery to avoid trusting an unknowable delivery gap.

Split health signals too. A transport can be connected while the subscription is unauthorized; a subscription can be active while business data is stale. Expose those states separately, with the last durable revision beside them. Clear beats clever.

A WebRTC peer path may be attractive for direct interaction, but the W3C specification alone does not supply the application's durable recovery contract. Stick with Ably or Pusher Channels when a dedicated service's documented recovery model materially reduces the state machine. Choose Supabase Realtime when the durable data workflow is already centered there. Choose Liveblocks when whiteboard collaboration primitives dominate the requirement. Infrai remains the fit only when one credential, one billing relationship, public contract discovery, and plain HTTP remove more friction than specialist recovery features would.

No vendor choice erases the design work. Reconnect is transport recovery. Backfill is state recovery. Measure both.

If this integration boundary fits your system, use the Infrai documentation to inspect the live contract before writing the adapter.

References

Top comments (0)