eventsource-client

repository·main·Indexed 41 days ago

https://github.com/rexxars/eventsource-client

A modern, streaming Server-Sent Events (SSE) client for Node.js, browsers, Deno, and Bun. Version 1.2.0 extends the standard EventSource API by supporting custom HTTP methods, request bodies, custom headers, and async iteration via for await...of. Built on fetch() and Web Streams, it provides a flexible interface for consuming event streams with configurable reconnection policies and advanced event handling.

Tokens
3.9K
Snippets
13
Records
16
Agent score
50%

What's inside eventsource-client

  1. What is eventsource-client and how does it differ from standard EventSource?

    main

    Unlike the standard browser EventSource API, eventsource-client is designed for modern streaming environments and provides several advanced capabilities:

    • Environment Agnostic: Works in Node.js, browsers, Deno, and Bun.
    • Flexible Request Methods: Supports any HTTP method (POST, PATCH, DELETE, etc.), whereas standard EventSource is limited to GET.
    • Customization: Supports custom headers, request bodies, and configurable reconnection policies.
    • Advanced Event Handling: Supports subscribing to any event name (even unknown ones), subscribing to error events, and setting an initial lastEventId.
    • Modern APIs: Built on fetch() and Web Streams.
  2. How to use the EventSource client

    main

    The eventsource-client package provides a way to consume Server-Sent Events (SSE) using modern web standards like fetch and AsyncIterators.

    While the core createEventSource function is marked as @internal and intended for use by environment-specific entry points, the client instance it returns provides the following public interface:

    Consumption Patterns

    Use the for await...of syntax to consume messages as they arrive. This is the most idiomatic way to handle the stream.

    2. Callback Pattern

    Provide an onMessage callback in the options object to handle incoming EventSourceMessage objects.

    Client Instance API

    • connect(): Initiates or re-establishes the connection.
    • close(): Manually closes the connection and stops reconnection attempts.
    • lastEventId: A getter that returns the id of the last received message.
    • url: A getter that returns the current URL (useful if redirects occurred).
    • readyState: A getter that returns the current state (CONNECTING, OPEN, or CLOSED).
    • [Symbol.asyncIterator]: Enables the for await...of loop.
    // Example: Using Async Iteration
    // Note: You would typically import the environment-specific client,
    // not the internal createEventSource directly.
    
    for await (const message of eventSource) {
      console.log('Received message:', message.data);
      console.log('Message ID:', message.id);
    }
  3. Use the `onMessage` callback to handle events

    main

    Instead of an async iterator, you can provide an onMessage callback in the configuration object. This callback receives an object with {data, event, id}.

    You can check the connection state using es.readyState (which returns 'open', 'closed', or 'connecting') and access the current es.lastEventId.

    import {createEventSource} from 'eventsource-client'
    
    const es = createEventSource({
      url: 'https://my-server.com/sse',
    
      onMessage: ({data, event, id}) => {
        console.log('Data: %s', data)
        console.log('Event ID: %s', id) // Note: can be undefined
        console.log('Event: %s', event) // Note: can be undefined
      },
    
      // your `fetch()` implementation of choice, or `globalThis.fetch` if not set
      fetch: myFetch,
    })
    
    console.log(es.readyState) // `open`, `closed` or `connecting`
    console.log(es.lastEventId)
    
    // Later, to terminate and prevent reconnections:
    es.close()
  4. Use the async iterator pattern to consume events

    main

    You can consume SSE messages using the for await...of syntax. Each yielded object contains {data, event, id}.

    Important: The connection is not automatically closed when you break out of the loop. You must manually call es.close() to terminate the connection and prevent further reconnections.

    import {createEventSource} from 'eventsource-client'
    
    const es = createEventSource({
      url: 'https://my-server.com/sse',
    
      // your `fetch()` implementation of choice, or `globalThis.fetch` if not set
      fetch: myFetch,
    })
    
    let seenMessages = 0
    for await (const {data, event, id} of es) {
      console.log('Data: %s', data)
      console.log('Event ID: %s', id) // Note: can be undefined
      console.log('Event: %s', event) // Note: can be undefined
    
      if (++seenMessages === 10) {
        break
      }
    }
    
    // IMPORTANT: EventSource is _not_ closed automatically when breaking out of
    // loop. You must manually call `close()` to close the connection.
    es.close()
  5. Handle SSE comments with `onComment`

    main

    If the server sends comments (lines starting with :), you can capture them by providing an onComment callback. Note that a single leading space will be trimmed from the comment string.

    import {createEventSource} from 'eventsource-client'
    
    const es = createEventSource({
      url: 'https://my-server.com/sse',
      onComment: (comment: string) => {
        /* a single, leading space will be trimmed if present */
        /* eg `: hello` and `:hello` will both yield `hello` */
      },
    })
  6. Configure EventSourceOptions

    main

    When initializing an EventSourceClient, you provide an EventSourceOptions object. This object allows you to configure the connection URL, lifecycle callbacks, and underlying fetch request parameters.

    Lifecycle Callbacks

    • onMessage: Fired when a new EventSourceMessage is received.
    • onConnect: Fired when the connection is established (including reconnects).
    • onScheduleReconnect: Fired when a reconnect is scheduled. Receives an object { delay: number } indicating the millisecond delay.
    • onDisconnect: Fired when the connection is broken (the client will still attempt to reconnect unless close() is called).
    • onComment: Fired when a comment is received. The comment string has one leading space trimmed if present.

    Request Configuration

    Options like headers, mode, credentials, method, redirect, referrer, and referrerPolicy are passed to the underlying fetch implementation to control the HTTP request.

    const options: EventSourceOptions = {
      url: 'https://example.com/events',
      onMessage: (event) => console.log('New message:', event),
      onConnect: () => console.log('Connected!'),
      onScheduleReconnect: ({ delay }) => console.log(`Reconnecting in ${delay}ms`),
      headers: {
        'Authorization': 'Bearer token'
      }
    };
  7. Supported engines for eventsource-client

    main

    The client requires environments that support ReadableStream, TextDecoderStream, and Symbol.asyncIterator. Supported engines include:

    • Node.js: >= 18
    • Deno: >= 1.30
    • Bun: >= 1.1.23
    • Chrome: >= 63
    • Safari: >= 11.3
    • Firefox: >= 65
    • Edge: >= 79
  8. Use the EventSourceClient API

    main

    The EventSourceClient is the primary interface for managing an EventSource connection. It supports both callback-based and async iterator-based consumption of messages.

    Core Methods

    • close(): Closes the connection and prevents automatic reconnection.
    • connect(): Manually triggers a connection attempt. This is typically only needed after calling close(), if the server sent an HTTP 204, or if the server responded with a non-retryable error.

    Properties

    • readyState: The current connection state ('open' | 'connecting' | 'closed').
    • lastEventId: The ID of the last seen event, or the initialLastEventId if no messages have been received yet.
    • url: The current URL of the connection (reflects redirects if allowed).
    // Using Async Iterator
    for await (const message of client) {
      console.log(message);
    }
    
    // Manual control
    client.close();
    client.connect();
  9. Create an EventSource client with createEventSource()

    main

    Use createEventSource() to instantiate a new client. This is the primary entrypoint for the library. You can pass either an EventSourceOptions object or a URL (or a URL string) as the argument. This function uses default environment abstractions, making it suitable for environments where standard Web APIs like ReadableStream are globally available.

    import { createEventSource } from 'eventsource-client';
    
    // Using a URL string
    const client = createEventSource('https://example.com/events');
    
    // Using an options object
    const clientWithOptions = createEventSource({
      url: 'https://example.com/events',
      // ... other EventSourceOptions
    });
  10. Create an EventSource client in Node.js with createEventSource()

    main

    In a Node.js environment, use createEventSource to instantiate a new EventSourceClient. This function is a wrapper around the core client that automatically provides the necessary Node.js stream abstractions. You can pass an EventSourceOptions object, a URL object, or a string URL as the argument.

    import { createEventSource } from 'eventsource-client/node';
    
    const client = createEventSource('https://example.com/events');
  11. Reference: EventSourceOptions

    main

    Configuration options for the EventSourceClient.

    export interface EventSourceOptions {
      url: string | URL;
      onMessage?: (event: EventSourceMessage) => void;
      onConnect?: () => void;
      onScheduleReconnect?: (info: {delay: number}) => void;
      onDisconnect?: () => void;
      onComment?: (comment: string) => void;
      initialLastEventId?: string;
      fetch?: FetchLike;
      headers?: Record<string, string>;
      mode?: 'cors' | 'no-cors' | 'same-origin';
      credentials?: 'include' | 'omit' | 'same-origin';
      body?: any;
      method?: string;
      redirect?: 'error' | 'follow';
      referrer?: string;
      referrerPolicy?: ReferrerPolicy;
    }