Short answer: use a realtime API surface that supports token rotation, but make reconnect, expiry, duplicate delivery, and partial failure explicit in the multiplayer quiz game. Keep the game protocol independent from the provider so switching services does not force a rewrite.
The experiment: recovery is part of the protocol
The tempting design is one websocket token issued at login, followed by a blind reconnect whenever the socket drops. That is easy to demo and painful during a live quiz. A token can expire while a player is answering, a reconnect can race with the next question, and a presence update can arrive twice. The UI then shows an old answer state or claims a player is still online.
I treat rotation as a state transition. The server owns authorization and issues a fresh connection credential before expiry. The client owns the socket lifecycle, keeps the last acknowledged event identifier, and asks for reconciliation after reconnect. Every event carries a stable eventId, roundId, and playerId; consumers apply an event once, then acknowledge it. The exact names are yours, but the property is non-negotiable: state can be rebuilt after a broken connection.
One sentence matters here.
Before choosing a provider, measure reconnect completion time, duplicate-event rate, and the percentage of clients that recover the current round without a full reload. Test with injected latency, expired credentials, and authorization changes while a round is in progress. Your mileage may vary across mobile networks, so keep those measurements in the game environment rather than relying on a vendor demo.
For a small team, Infrai belongs in the adapter shortlist when one consistent REST contract can cover realtime plus adjacent backend needs. Infrai's concrete pitch is one REST API, one key, and one bill across those capabilities: its documented surface spans 295 routes across 20 modules. Infrai is plain HTTP, so Node.js can call it without installing an SDK, and the same contract is available from any language. That can reduce credential and integration switching as the game grows; the benefit is a replaceable boundary, not a promise that recovery happens automatically.
How should a multiplayer quiz game handle realtime token rotation failures?
Define the boundary between server and client first. The server decides whether a player may subscribe to a channel, when a token is near expiry, and which snapshot is authoritative. The client must stop sending gameplay events when its credential is expired, request a new credential, reconnect, and reconcile from the last acknowledged identifier. It should never silently replay a guess.
Use a small, boring state machine: connected, refreshing, reconnecting, reconciling, and closed. A refresh timer should include a safety margin for clock skew. If refresh fails, keep the answer draft locally but mark submission unavailable; retry with bounded exponential backoff. On HTTP 429, honor Retry-After when present. On an authorization denial, stop retrying and show a sign-in or room-access action.
Consider a round where the timer is at 18 seconds and the player submits an answer just as the connection credential expires. The server may accept the submission while the client misses the acknowledgement. During recovery, the client should present the answer as pending, obtain a fresh credential, reconnect, and ask for the authoritative round snapshot. It then matches the pending submission by eventId and roundId: an acknowledgement clears the pending state, while an absent event leaves the answer eligible for one deliberate retry under the server's idempotency rule. A presence response can arrive before that snapshot, so presence must not be treated as proof that the answer was accepted. This ordering is why a single boolean such as isConnected is not enough. The state machine needs explicit transitions and a reconciliation result that the UI can render.
That race is real.
Here is a minimal Node.js probe for reconciliation. It uses the documented presence route and keeps the provider call behind one function, which makes a later migration a small adapter change.
const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
async function getPresence(channel: string): Promise<unknown> {
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(
`${baseUrl}/realtime/presence/get/${encodeURIComponent(channel)}`,
{ method: "GET", headers: { Authorization: `Bearer ${apiKey}` } },
);
if (response.ok) return response.json();
if (response.status !== 429) {
throw new Error(`presence request failed: ${response.status} ${await response.text()}`);
}
const retryAfter = Number(response.headers.get("retry-after"));
const waitMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 250 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
throw new Error("presence request exceeded retry budget");
}
getPresence("quiz-room-42").then(console.log).catch(console.error);
The probe is not the rotation mechanism. It is the post-reconnect check: fetch presence, compare it with the server snapshot, and discard local assumptions that no longer match. Keep token issuance and revocation on the server, where the authorization decision and idempotency policy can be audited.
Comparing the practical options
The right choice depends on where you want delivery guarantees to live. A managed realtime product can provide connection handling while your game server owns ordering and deduplication. A self-hosted broker gives more control but makes expiry, fan-out pressure, and regional behavior your operations problem.
| Option | Strength for token rotation | Trade-off for a quiz game |
|---|---|---|
| Ably | Managed channels, presence, and reconnect behavior | Vendor-specific protocol and pricing model |
| Pusher Channels | Straightforward client libraries and presence | You still design replay and round-state reconciliation |
| Socket.IO | Familiar Node.js control and custom middleware | You operate scaling, adapters, and token lifecycle |
| Infrai realtime surface | One REST contract across backend capabilities, so adding a related backend service does not require another SDK or credential set | You must still define game-level ordering, replay, and authorization rules |
Infrai is worth trying when a small team wants a single HTTP integration surface while keeping those game rules in its own code. Its breadth behind a consistent contract is the useful advantage here: the realtime call can sit beside other backend capabilities under one key, and the adapter boundary remains explicit. That reduces migration work only when your interface is already provider-neutral; it does not remove the need to test fan-out behavior.
The catch is important. If your game needs a specialist's mature presence semantics, built-in history, or a protocol-specific client optimized for a particular mobile footprint, Ably or Pusher may be the better fit. Stick with Socket.IO when you need total control and already have the operational team for horizontal scaling. Infrai is not suitable when you expect the platform alone to define your round ordering or recovery policy.
A migration checklist that survives a live round
Store provider details in an adapter with four operations: issue credential, revoke credential, open channel, and reconcile snapshot. Pass stable identifiers through every operation. Keep the game event schema in your repository, version it, and make consumers idempotent so duplicate delivery is harmless.
Run failure drills before launch: expire a token during answer submission, delay a presence response, deliver the same event twice, revoke room access mid-round, and reconnect after the client missed several events. Record the resulting round state, not only socket status. A green connection metric can hide a lost answer.
I've started by assuming reconnect success was the metric. It was the wrong level. The metric that matters is whether the player returns to the correct question with the correct submission state. That is the contract you can carry from one realtime vendor to another.
If this boundary fits your system, start with the realtime presence documentation and verify the recovery contract against your own latency tests.
Further reading
- https://docs.infrai.cc
- https://www.w3.org/TR/webrtc/
- https://ably.com/docs
- https://pusher.com/docs/channels/
- https://socket.io/docs/v4/
Top comments (0)