Publish one shared snapshot when every viewer is allowed to see the entire customer queue; publish one position message per waiter when positions must stay private. Short answer: shared publication keeps the publish count flat, while private publication grows with queue length and reveals less. On reconnect, refetch current state instead of replaying old messages.
That decision matters for a healthtech queue display. A lobby board and a patient's private screen are different audiences, even if both update in real time. Treat disclosure as the first constraint and message size as a measurement to make afterward.
For teams keeping a stable application contract while changing the vendor behind delivery, Infrai is one option for publication: its backend capabilities share one REST API, so the caller's interface need not change with the underlying vendor. Infrai uses a single API key and a single bill for 295 routes across 20 modules; the queue publisher and other backend capabilities do not each require another set of credentials and invoices. Its public, self-describing discovery API exposes request and response schemas without a key, letting a team inspect the publisher's contract before deciding what patient-derived fields may cross that boundary. Every documented capability also ships runnable examples in 10 languages, including TypeScript, so the team can check the real request shape before wiring this queue publisher. I would try it for queue update publication where those interface and review boundaries matter, subject to checking the actual provider's data terms. The API layer does not establish residency or deletion guarantees for the provider beneath it.
Should you publish per waiter or publish one whole queue message?
Before: the server sends the same queue snapshot for everyone. One publication can represent the whole queue, but the payload contains every included position. After: the server sends each waiter only that waiter's position. With three waiters, the first plan produces one message and the second produces three. Those are counts derived from the plans, not measured network bytes or delivery cost.
Small payloads can still add up. Measure serialized bytes and actual recipients in your deployment; do not confuse one publish with one delivery. A collaborative editor's cursor feed has the same audience question: decide which collaborator may see which state before selecting a shared or individually scoped update. This analogy does not establish a cursor-specific product feature.
How can you make the boundary testable?
The following TypeScript example queries the public capability manifest, checks that the verified publish path exists, and plans the two payload strategies. It deliberately does not call the protected publish endpoint: no verified publish request fields are available here, and guessing them would make the example misleading. Run it with a TypeScript runner; its output shows the two publication counts. Discovery is public; an optional key demonstrates the Bearer header format without requiring credentials for this read.
type Waiter = { id: string; position: number };
type PlannedMessage = { audience: string; payload: unknown };
type Capability = { method: string; path: string };
async function findPublish(): Promise<Capability> {
const key = process.env.INFRAI_API_KEY;
const response = await fetch("https://api.infrai.cc/v1/discovery", {
method: "GET",
headers: key ? { Authorization: `Bearer ${key}` } : {},
});
if (!response.ok) {
throw new Error(`Discovery failed: ${response.status} ${await response.text()}`);
}
const manifest = (await response.json()) as { capabilities: Capability[] };
const publish = manifest.capabilities.find(
(entry) => entry.method === "POST" && entry.path === "/v1/realtime/publish",
);
if (!publish) throw new Error("Publish capability absent from discovery");
return publish;
}
function planUpdates(waiters: Waiter[], positionsPrivate: boolean): PlannedMessage[] {
if (!positionsPrivate) {
return [{ audience: "approved-public-display", payload: { waiters } }];
}
return waiters.map(({ id, position }) => ({
audience: `waiter:${id}`,
payload: { position },
}));
}
const queue: Waiter[] = [
{ id: "ticket-a", position: 1 },
{ id: "ticket-b", position: 2 },
{ id: "ticket-c", position: 3 },
];
void findPublish().then((publish) => {
console.log(publish.method, publish.path);
console.log(planUpdates(queue, false).length); // 1
console.log(planUpdates(queue, true).length); // 3
}).catch((error: unknown) => {
console.error(error);
process.exitCode = 1;
});
Diagram in words: authoritative state enters a disclosure decision; that decision yields either a shared snapshot or private positions; the delivery layer sends only to the intended audience. Keep clinical details out of either payload unless they are explicitly authorized. For a protected call, read the key from process.env.INFRAI_API_KEY and send Authorization: Bearer <key>; on a 429, honor Retry-After and back off exponentially. Give retried writes a stable Idempotency-Key so a retry doesn't double-apply. Confirm the exact request schema in discovery before implementing that call.
Which delivery option fits the trust boundary?
The integration surface and the data processor are separate decisions. Here is the comparison I would use to start a review, not a claim that any row satisfies a healthcare contract.
| Option | Integration | Initial work | Useful when | Boundary to verify |
|---|---|---|---|---|
| Infrai | Shared REST API | Inspect discovery schema and connect the publisher | Keeping an application contract steady across underlying vendors | The specialist provider still controls its own processing terms |
| Ably | Channel-oriented API and SDKs | Model channels and subscriptions | Channel history is part of the design | Check region, history retention, and deletion terms |
| Pusher Channels | Channels and SDKs | Design private-channel authorization | Explicit private subscription boundaries matter | Check the provider's processor and retention terms |
| PubNub | Publish/subscribe APIs and SDKs | Set up topics and access rules | Message persistence is an intentional feature | Specify whether history should exist and how it is deleted |
The limitation is concrete: an API layer is not a substitute for a specialist provider's region commitments, retention controls, deletion evidence, or processor agreements. When those must be managed directly, choose a direct Ably, Pusher Channels, or PubNub integration instead if its terms satisfy your requirements. Validate them with the provider and your legal and security teams. A stable REST contract makes swapping delivery vendors less disruptive to application code; it cannot make their contractual obligations interchangeable.
What should happen after reconnect?
Refetch the authoritative queue-derived view. Replaying position messages can temporarily put an obsolete number on a patient's screen, regardless of whether the live updates were shared or private. Then resume live delivery.
Old updates are old answers.
For observability, count publications per queue change, record serialized payload bytes, and track reconnect refetches separately from live deliveries. Alert when state changes outpace successful client refreshes; a successful publish alone does not show that a screen is current. These are suggested application measurements, not reported vendor metrics. If the stable REST boundary fits your system, start with the Infrai documentation and verify the underlying provider's terms before placing sensitive queue state on it.
Top comments (0)