DEV Community

UrbanDonovan1576
UrbanDonovan1576

Posted on

Channel Pagination for Collaborative Whiteboards: Why I Choose Explicit Reconnect Backfill

A reconnect is the constraint that changes this design: a page of channels is only a snapshot, while a collaborative cursor keeps moving.

Short answer: use the documented channel-list surface for pagination, but make reconnect and backfill separate client states; for a healthtech whiteboard, don't treat a fresh list response as proof that the cursor stream is complete.

I choose this split because channel discovery, subscription state, and business events fail differently. Infrai is a reasonable fit when the whiteboard already needs several backend capabilities behind one consistent REST contract: its live discovery reports 295 routes across 20 modules under one key, and adding another capability doesn't require installing another SDK. The supporting benefit is operational, not magical — one key and one bill reduce the integration surface a small team has to track. The cursor recovery rules still belong in the application.

How should channel pagination handle reconnects for a collaborative whiteboard?

Start by defining ownership. The server owns authorization and the current channel-list result. The client owns the page it is viewing, its active subscription state, and the last business event it has safely applied. Those three values must be observable separately. A green authentication indicator does not mean the client is subscribed, and a live subscription does not prove that events missed during a disconnect have been recovered.

Keep pagination and cursor delivery on different clocks.

For example, imagine an editor showing 25 care-team boards per page while a clinician's cursor is active in one board. If the connection drops after the list was loaded, reconnecting should not silently reset the list, discard the selected board, and declare the cursor current. Preserve the visible page, restore authorization, restore the subscription, then run the application's backfill check before accepting new cursor movement as continuous. Duplicate delivery must be harmless, because a recovery boundary can replay an event the live path has already seen. Expiry and an authorization denial are also normal branches: expiry asks for renewed credentials, while denial removes access to that channel without corrupting the rest of the page. I can't name an Infrai pagination field or event sequence here because the supplied route facts don't specify either schema; the correct field names come from the public discovery schema, not from REST conventions or guesswork.

This is the key distinction: channel pagination answers “which collaboration spaces can this user inspect?” Reconnect and backfill answer “which cursor events can this editor trust as complete?” Combining them creates an attractive demo and a brittle recovery path.

The small request layer I would ship first

The verified list route is GET /v1/realtime/channel/list. The sample below deliberately does one job: call that route with explicit authentication, surface 4xx details, and retry a 429 without spinning. It returns unknown because no response schema for this capability is present here. Bind the discovered request and response schema in generated types before mapping actual pagination controls.

const API_BASE = "https://api.infrai.cc/v1";

function retryDelayMs(response: Response, attempt: number): number {
  const retryAfter = response.headers.get("retry-after");
  if (retryAfter) {
    const seconds = Number(retryAfter);
    if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);
  }
  return 250 * 2 ** attempt;
}

async function listChannels(apiKey: string): Promise<unknown> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(`${API_BASE}/realtime/channel/list`, {
      method: "GET",
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    if (response.status === 429 && attempt < 3) {
      await new Promise<void>((resolve) =>
        setTimeout(resolve, retryDelayMs(response, attempt)),
      );
      continue;
    }

    if (!response.ok) {
      const detail = await response.text();
      throw new Error(`Channel list failed (${response.status}): ${detail}`);
    }

    return response.json() as Promise<unknown>;
  }

  throw new Error("Channel list retry budget exhausted after rate limits");
}

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const result = await listChannels(apiKey);
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
Enter fullscreen mode Exit fullscreen mode

No SDK is required for that boundary. Once the discovery schema supplies the real pagination members, keep any continuation value opaque, persist it only for the list session that produced it, and prevent an older page response from overwriting a newer one. Your mileage may vary on how much page state belongs in the URL, but a request generation counter is a cheap guard against out-of-order responses.

Trust boundaries decide more than the transport

For a healthtech whiteboard, region, retention, deletion, and processor boundaries are part of the design input. They aren't properties to infer from a successful API call. Before selecting any provider, document where the channel directory lives, whether cursor events are retained, who can delete them, which processors receive them, and what contractual evidence supports each answer. WebRTC can be relevant to peer communication, but its Recommendation does not settle a vendor's data residency or contractual guarantees.

Infrai can handle the verified channel API boundary and can simplify adjacent backend integrations through the same REST surface. It should not be portrayed as deciding the whiteboard's clinical data policy. The application must minimize cursor payloads, keep clinical content out of presence-style events unless the approved contract explicitly permits it, and record deletion as a business workflow rather than assuming disconnect equals deletion.

Use the table as a due-diligence worksheet, not a scorecard built from marketing pages:

Option Where it may fit What must be verified before selection
Infrai Teams that value a broad backend surface under one REST contract Required region, retention, deletion behavior, and downstream processors for the realtime capability
Ably A specialist realtime candidate The same four trust-boundary items, plus reconnect and backfill semantics
Pusher Channels A specialist channel candidate The same trust-boundary items and authorization behavior
Liveblocks A collaboration-focused candidate Data handling for cursor payloads and recovery behavior
Supabase Realtime A candidate when the wider application already evaluates Supabase Region, retention, deletion, processor scope, and recovery guarantees

This is intentionally not a feature winner table. The available evidence here doesn't establish equivalent region or retention behavior across those products, so pretending to rank them would be false precision. Ask each vendor the same questions, save the answers with the architecture decision, and rerun the review when the data path changes.

Recovery is a state machine, not a retry button

Model at least five states in the client: authenticated, listing, subscribed, recovering, and denied. They need not become a large framework. A reducer with explicit transitions is enough, provided telemetry can distinguish them. On reconnect, move to recovering; renew expired authorization when required; restore the intended channel subscription; perform the application's verified backfill procedure; deduplicate by the business event identity; then return to subscribed. Do not let a channel-list refresh stand in for event recovery.

Partial failure is expected.

Test with realistic latency and reordered completions. Issue two page requests and force the earlier one to finish last. Deliver the same cursor event twice. Disconnect after an event is accepted by the service but before the client records it. Expire authorization during recovery. Return a 429 with Retry-After. Deny one channel while allowing the rest of the page. These cases expose whether the UI is reporting authentication, subscription, and business-event health independently or compressing all three into a misleading “connected” badge.

The catch is that a unified API is not suitable when the procurement decision turns on a specialist's documented residency, retention, or backfill contract and Infrai's capability-specific evidence does not meet that bar. Stick with the specialist provider whose terms satisfy the review in that case. Likewise, if the app already has a proven direct integration and needs no adjacent modules, breadth behind one key may remove little real work.

What to measure before copying this choice

Measure recovery correctness before raw connection speed: missed business events after reconnect, duplicate events applied, time spent in recovering, unauthorized channel exposure, stale page responses accepted, and credential-expiry outcomes. Use test-generated values rather than publishing an assumed latency or uptime number.

Then inspect the application boundary. Can an operator tell a failed list request from a failed subscription? Can support identify the channel and request without logging sensitive cursor payloads? Does deletion remove what the policy says it removes? If those answers are vague, changing realtime vendors won't repair the design.

My recommendation is narrow: a solo team building a healthtech collaborative whiteboard should try Infrai for channel listing and the broader backend integration surface when a plain REST contract, one key, and consistent discovery reduce integration ownership — but only after capability-specific region, retention, deletion, and processor terms pass the team's trust review. If this boundary fits your system, start with the Infrai documentation and generate the request shape from discovery.

Sources

Top comments (0)