fetch-event-source

repository·main·Indexed 25 days ago

https://github.com/azure/fetch-event-source

A robust alternative to the browser's native EventSource API for consuming Server-Sent Events (SSE). It leverages the Fetch API to support custom headers, request bodies, various HTTP methods, and advanced retry logic via lifecycle callbacks like onopen, onmessage, onclose, and onerror. Version 2.0.1 targets ES2017 and is compatible with evergreen browsers.

Tokens
2.7K
Snippets
9
Records
16
Agent score
84%

What's inside @microsoft/fetch-event-source

  1. Implement custom retry and error handling strategies

    main

    You can control the connection lifecycle and retry logic using the following callbacks:

    • onopen(response): Validate the response (e.g., check status codes or content-type) before parsing. Throwing an error here triggers the onerror handler.
    • onmessage(msg): Handle incoming messages. You can throw an error inside this callback to trigger the onerror handler.
    • onclose(): Triggered when the server closes the connection. Throwing an error here allows you to implement custom retry logic.
    • onerror(err): The central error handler. To stop the connection entirely, rethrow a fatal error. To trigger an automatic retry, simply do nothing or return a specific retry interval.
    class RetriableError extends Error { }
    class FatalError extends Error { }
    
    fetchEventSource('/api/sse', {
        async onopen(response) {
            if (response.ok && response.headers.get('content-type') === EventStreamContentType) {
                return; // everything's good
            } else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
                // client-side errors are usually non-retriable:
                throw new FatalError();
            } else {
                throw new RetriableError();
            }
        },
        onmessage(msg) {
            // if the server emits an error message, throw an exception
            // so it gets handled by the onerror callback below:
            if (msg.event === 'FatalError') {
                throw new FatalError(msg.data);
            }
        },
        onclose() {
            // if the server closes the connection unexpectedly, retry:
            throw new RetriableError();
        },
        onerror(err) {
            if (err instanceof FatalError) {
                throw err; // rethrow to stop the operation
            } else {
                // do nothing to automatically retry. You can also
                // return a specific retry interval here.
            }
        }
    });
  2. Browser compatibility and polyfills

    main

    The library targets ES2017 and is compatible with evergreen browsers (Chrome, Firefox, Safari, Edge). For older versions of Edge (pre-v79), you must polyfill TextDecoder using fast-text-encoding.

    require('fast-text-encoding');
  3. Use fetchEventSource for basic SSE consumption

    main

    Replace the standard EventSource API with fetchEventSource to consume an event stream. This provides a more modern interface and allows for easier integration with the Fetch API.

    import { fetchEventSource } from '@microsoft/fetch-event-source';
    
    await fetchEventSource('/api/sse', {
        onmessage(ev) {
            console.log(ev.data);
        }
    });
  4. Configure advanced request options with fetchEventSource

    main

    Unlike the standard EventSource API, fetchEventSource allows you to use any HTTP method, custom headers, and a request body. It also supports AbortController signals for cancellation.

    const ctrl = new AbortController();
    fetchEventSource('/api/sse', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            foo: 'bar'
        }),
        signal: ctrl.signal,
    });
  5. Control retry behavior with onerror

    main

    The onerror callback in fetchEventSource is the primary mechanism for managing connection failures and retries.

    • To retry with a specific delay: Return a number representing milliseconds (e.g., return 2000;).
    • To retry using the default/current interval: Return undefined, null, or void. The default interval is 1000ms.
    • To stop retrying (Fatal Error): Rethrow the error inside the onerror callback. This will cause the fetchEventSource promise to reject.
  6. Use fetchEventSource to consume Server-Sent Events (SSE)

    main
    The fetchEventSource function is the primary entrypoint for initiating a connection to an SSE stream. Unlike the native EventSource API, fetchEventSource allows you to use custom headers, request methods (like POST), and a request body, making it suitable for complex authentication or data-driven stream requests.
  7. Configure fetchEventSource options

    main

    The fetchEventSource function accepts a FetchEventSourceInit object to configure the connection, message handling, and retry logic.

    Key configuration options include:

    • headers: A Record<string, string> of request headers. Note that fetchEventSource only supports this format.
    • onopen: A callback invoked when a response is received. Use this to validate the response (e.g., checking status codes or content types). If not provided, it defaults to validating that the content-type is text/event-stream.
    • onmessage: A callback invoked for all events received, including those with custom event fields. Receives an EventSourceMessage.
    • onclose: A callback invoked when a response finishes.
    • onerror: A callback invoked on errors. It controls the retry strategy:
      • Return a number (milliseconds) to specify a custom retry interval.
      • Return null, undefined, or void to use the current retry interval (defaults to 1000ms).
      • Rethrow the error to stop the entire operation and treat the error as fatal.
    • openWhenHidden: If true, the request stays open even when the document is hidden. If false (default), the request is aborted and automatically reopened when the document becomes visible again.
    • fetch: The fetch implementation to use (defaults to window.fetch).
  8. Handle visibility changes and openWhenHidden

    main

    By default, fetchEventSource manages browser visibility to save resources. When the document becomes hidden (e.g., user switches tabs), the current request is aborted. When the document becomes visible again, the request is automatically reopened.

    To prevent this behavior and keep the connection alive in the background, set openWhenHidden: true in the configuration object.

  9. Parse byte chunks into lines with getLines

    main

    The getLines function provides a way to parse arbitrary byte chunks into EventSource line buffers. It handles buffering across chunks and identifies lines ending in \r, \n, or \r\n.

    Parameters:

    • onLine: A callback function (line: Uint8Array, fieldLength: number) => void called for each new line found. fieldLength indicates the length of the field name (the part before the colon).

    Returns:

    • A function onChunk(arr: Uint8Array) that must be called for each incoming byte chunk to continue parsing.
    export function getLines(onLine: (line: Uint8Array, fieldLength: number) => void)
  10. Parse line buffers into EventSourceMessages with getMessages

    main

    The getMessages function parses line buffers (from getLines) into structured EventSourceMessage objects. It handles the logic for aggregating data fields, setting id, event, and retry values, and detecting the end of a message (indicated by an empty line).

    Parameters:

    • onId: Callback called when an id field is parsed.
    • onRetry: Callback called when a retry field is parsed.
    • onMessage (optional): Callback called when a complete EventSourceMessage is assembled.

    Returns:

    • A function onLine(line: Uint8Array, fieldLength: number) that must be called for each incoming line buffer.
    export function getMessages(
        onId: (id: string) => void,
        onRetry: (retry: number) => void,
        onMessage?: (msg: EventSourceMessage) => void
    )