VentStream can replay GraphQL subscriptions after a disconnect or operation
replacement when the gateway uses JetStream or Redis Streams. Reliability is
operation-scoped:
- Start the first subscription without a cursor.
- Process each event successfully.
- Persist that operation’s opaque
cursor.
- Recreate the operation with
resumeFromCursor.
The gateway resumes strictly after that cursor. Multiple operations can share
one graphql-transport-ws connection while maintaining independent cursors.
This provides at-least-once processing within the broker retention window.
Handlers must be idempotent because a disconnect between applying a side effect
and saving its cursor can replay the event.
Checkpoint only after every required side effect succeeds. Saving a cursor when
the event is merely received can permanently skip unfinished work.
Typed schema contract
Author the subscription normally. resumeFromCursor is a reserved operation
argument that VentStream adds to the effective runtime schema. VentStream also
adds cursor and seq to an inline result type when those fields are absent:
The effective schema exposed through GraphQL introspection includes:
cursor is provider-neutral. Keep it as a string: JetStream currently emits
decimal strings while Redis Streams emits values such as
rs:1712345678901-0.
For reliability-critical operations, do not author application fields named
cursor or seq. Existing schemas that define either name retain their
application mapping for backward compatibility, which shadows the corresponding
broker checkpoint field.
First connection
The client has no checkpoint on its first subscription. Omit
resumeFromCursor or pass null:
No cursor means live-only delivery from the time the operation attaches. It
does not request historical events.
Requirements
- Run the engine with the
graphql role and a replay-capable provider:
JetStream or Redis Streams.
- Select
cursor and stable event id in every reliability-critical
operation.
- Maintain one cursor store per logical operation and argument scope. An order
stream and an audit stream must not overwrite each other’s checkpoint.
- Use idempotent handlers keyed by event
id.
- Size broker retention for the longest outage plus worst-case catch-up time.
- Treat
RESUME_EXPIRED and INVALID_CURSOR as terminal recovery decisions;
never silently clear a bad checkpoint.
Install Apollo
Store cursors per operation
Keep the storage boundary small so browser localStorage can later be replaced
with IndexedDB or a backend checkpoint service:
For side effects that change durable business state, store the event ID, side
effect, and cursor transactionally whenever possible.
Multiplex operations on one Apollo connection
graphql-ws normally replays an active operation with the variables it had
when it was first created. Those variables may contain an old cursor. Therefore
the reconnect hook below removes active observers and recreates every operation
after the new socket is acknowledged, reading the latest cursor from each
store.
The getCurrentAccessToken, apply*Idempotently, report*, and
readGraphQLErrorCode functions in this example are application-owned
integration points. Implement them using your identity provider, durable
business store, and telemetry system.
VentStream’s current gateway only requires a non-empty token and trusts the
tenant asserted during connection initialization. Put the gateway behind an
authenticating proxy that validates the token and binds its claims to the
allowed tenant before exposing it to untrusted clients.
The two promise queues preserve processing order independently. An order
handler cannot advance the audit cursor, and a slow audit handler does not
prevent a successfully processed order from checkpointing. The generation
guard drops queued work that has not started when recovery begins. A handler
already in progress cannot be cancelled safely, which is why durable side
effects still need event-ID idempotency.
Connection cursor compatibility
resume_from_cursor in connection_init remains supported. It is a fallback
for clients with one operation or clients that maintain one globally
coordinated checkpoint across every operation on the socket. When an operation
supplies resumeFromCursor, the operation value takes precedence.
For multiplexed applications, prefer operation cursors. They let operations
attach at different times without one operation acknowledging another
operation’s replay.
Recovery behavior
Production checklist
- Persist each operation cursor after processing, never before it.
- Recreate multiplexed operations with fresh cursor variables after reconnect.
- Make handlers idempotent using event
id.
- Expose connecting, live, recovering, terminal-error, replay-lag, and handler
failure states to application telemetry.
- Test socket termination while publishing and verify every event after each
operation’s checkpoint is eventually processed.
- Test a cursor outside retention and require an explicit rebuild.
- Size retention for the maximum outage plus worst-case replay time.
See Realtime brokers for provider configuration,
cursor semantics, retention, and gateway observability.