sseTransport
Reads realtime events from a server-sent events stream and publishes with a POST. Needs a stream endpoint, and a publish endpoint to publish to.
sseTransport opens one EventSource stream to read events and publishes with
a POST. It needs a server that names each event after its topic.
Signature
function sseTransport(options: SseTransportOptions): SseTransport
interface SseTransportOptions {
/** The stream URL; the topic filter and the token are appended to its query. */
url: string;
/** Where `publish` POSTs. Without it `publish` rejects. */
publishUrl?: string;
/** The bearer token for the stream (as `token`) and for the publish POST. */
getAuthToken?: () =>
| string
| null
| undefined
| Promise<string | null | undefined>;
/** Sent to `EventSource` and as `credentials: "include"` on the publish POST. */
withCredentials?: boolean;
/** Exponential backoff for a dropped stream. */
reconnect?: RealtimeReconnectConfig;
/** How long the stream stays open with no subscriber. Default 30000. */
idleDisconnectMs?: number;
/** The query parameter that carries the topics. Default "topics". */
topicFilterParam?: string;
/** Called for every transport error, like a listener from `onError()`. */
onError?: (error: RealtimeTransportError) => void;
/** Used for the publish POST. Default the global `fetch`. */
fetch?: typeof fetch;
}
/** A `RealtimeTransport` with every optional member, plus `close()`. */
interface SseTransport extends Required<RealtimeTransport> {
/** Closes the stream and sets `"disconnected"`; a later subscribe opens again. */
close(): void;
}Returns
Every member of RealtimeTransport, plus close(). The transport starts as
"idle", reads "connected" once the stream is open, and returns to "idle"
idleDisconnectMs after the last unsubscribe. A publish never opens a stream.
- The stream is a
GETonurlwith the subscribed topics in thetopicFilterParamquery parameter, comma separated. The server sends one named event per topic, the event name being the topic, anddataholding JSON of{ type, payload, meta }. - Subscribes made in the same tick share one open. Subscribing to a topic the open stream does not carry reopens it with the new topic added. Unsubscribing leaves the stream as it is, and events for a topic with no subscriber are dropped.
- Data that is not JSON is reported as
parse_failed. JSON with notypeis dropped. publishsends aPOSTtopublishUrlwith a JSON body of{ topic, event }and anAuthorization: Bearer <token>header when there is a token. A status outside 200 to 299 rejects it, as does afetchthat throws. Apublishrejects with a message namingpublishUrlwhen that option is missing.EventSourcecannot set request headers, so the token always goes into the stream URL astoken, which carries the same risk asauthMode: "query"onwebSocketTransport. Use a short-lived token.withCredentialsis passed toEventSourceand sends credentials on thePOST.fetchreplaces the global one for thePOSTonly.close()closes the stream and sets"disconnected". A later subscribe opens again.
Example
import { realtimeDataProvider, sseTransport } from "@chelone/core";
import { baseProvider } from "./baseProvider";
import { getSession } from "./session";
export const transport = sseTransport({
url: "https://api.example.com/realtime/stream",
publishUrl: "https://api.example.com/realtime/publish",
getAuthToken: async () => (await getSession())?.accessToken,
topicFilterParam: "t",
});
export const dataProvider = realtimeDataProvider(baseProvider, transport);See the realtime guide for the stream and publish shapes in full, the reconnect options and the Supabase transport.