Stream Chat JavaScript SDK

repository·master·Indexed 42 days ago

https://github.com/getstream/stream-chat-js

The official JavaScript SDK for the Stream Chat API, providing a client to build real-time chat applications in both frontend and backend environments. Features include user and channel management, file uploads with progress tracking, custom logging, token provider support for short-lived tokens, and webhook verification using verifyAndParseWebhook.

Tokens
26.9K
Snippets
84
Records
121
Agent score
53%

What's inside Stream Chat

  1. Handle webhook errors with `InvalidWebhookError`

    master

    The SDK uses a single unified error class, InvalidWebhookError, for all webhook-related failures (signature mismatch, malformed gzip, malformed base64, or invalid JSON).

    You can use err.message combined with the exported InvalidWebhookErrorMessages constants to distinguish between specific failure modes, such as a signature mismatch versus a malformed payload.

    const { InvalidWebhookError, InvalidWebhookErrorMessages } = require('stream-chat');
    
    try {
      const event = client.verifyAndParseWebhook(req.body, req.headers['x-signature']);
    } catch (err) {
      if (err instanceof InvalidWebhookError) {
        if (err.message === InvalidWebhookErrorMessages.signatureMismatch) {
          return res.sendStatus(401);
        }
        return res.sendStatus(400);
      }
      throw err;
    }
  2. How the Burst Simulator works

    master

    The extension works by injecting simulator.js into the active tab's main world using chrome.scripting.executeScript({ world: 'MAIN' }). This allows the simulator to access the page's window and React tree.

    Simulation Logic

    • User Pool: Builds a pool of fake users to simulate multi-user activity.
    • Event Generation:
      • Generates message.new events with varied text (3–50 words, emojis, URLs, line breaks) to force realistic layout work.
      • Generates reaction.new events targeting the newly created messages. It pre-applies latest_reactions, reaction_counts, reaction_scores, and reaction_groups to the event.message so channel_state.addReaction functions correctly.
    • Routing: Each event is routed through client.handleEvent({ data: jsonString }) to exercise the cost of JSON.parse on the WebSocket frame.
    • Pacing: Events are dispatched based on the ratePerSec setting, or all at once in a single requestAnimationFrame tick if the rate is 0.
  3. Use a Token Provider for short-lived tokens

    master

    For security best practices using short-lived tokens, you can pass an asynchronous function (a Token Provider) as the second argument to client.connectUser instead of a string.

    When the current token expires, the SDK will automatically call this provider function to fetch a new token and then reconnect the WebSocket connection.

    Important: Ensure your Token Provider function always resolves. If the function rejects, the connection may fail. It is recommended to wrap your token fetching logic in a retry mechanism to handle transient network issues.

    const client = StreamChat.getInstance('api_key');
    client.connectUser({ id: 'vishal' }, async () => await fetchTokenFromApi());
  4. Burst Simulator limitations

    master

    When using the Burst Simulator for performance testing, be aware of the following constraints:

    • Event Types: Only supports message.new and reaction.new. It does not support typing indicators, edits, deletes, threads, attachments, or mentions.
    • Reaction Scope: Reactions only target messages generated during the current burst, not existing channel state.
    • Channel Scope: Only one channel can be tested at a time (the one bound to window.streamChannel or auto-detected).
    • Abort Behavior: In rate per second: 0 (burst-all) mode, the entire batch is processed in a single requestAnimationFrame tick. In this mode, the Stop button is ineffective because the abort flag cannot be observed mid-run. Use a paced rate if you need the ability to abort.
  5. Extend Stream entities with custom data using module augmentation

    master

    Since version 9.0.0, the SDK uses TypeScript module augmentation instead of Generics to allow developers to add custom properties to Stream entities (like CustomMessageData or CustomUserData). This provides full type safety and code-completion for custom fields in your application.

    To implement this:

    1. Create a .d.ts declaration file.
    2. Import stream-chat.
    3. Use declare module 'stream-chat' to extend the relevant interfaces.
    4. Ensure the declaration file is loaded by your TypeScript configuration.
    // stream-custom-data.d.ts
    import 'stream-chat';
    
    declare module 'stream-chat' {
      interface CustomMessageData {
        custom_property?: number;
      }
      interface CustomUserData {
        profile_picture?: string;
      }
    }
    
    // index.ts
    // Now 'profile_picture' and 'custom_property' are type-safe and autocompleted
    await client.partialUpdateUser({
      id: 'vishal-1',
      set: { profile_picture: 'https://random.picture/1.jpg' },
    });
    
    const { message } = await channel.sendMessage({
      text: 'This is another test message',
      custom_property: 255,
    });
  6. How message composer attachment progress works

    master

    When using the message composer's attachment manager, upload progress is tracked by default (config.attachments.trackUploadProgress: true). Progress is stored in each attachment's localMetadata.uploadProgress (a value from 0–100).

    If you implement a custom doUploadRequest, the options argument provides:

    • onProgress(percent: number | undefined): Call this to update localMetadata.uploadProgress. If not called, progress stays at 0% until completion.
    • abortSignal: AbortSignal: The SDK provides this signal; you should forward it to your transport (e.g., axios signal or fetch signal) to allow the SDK to cancel the upload if the user removes the attachment or disconnects.
  7. Configure custom logging for the Stream Chat client

    master

    You can provide a custom logger function in the StreamChat.getInstance options to intercept and handle log messages for debugging. This is useful for routing logs to specific tools like console.log, reactotron, or other logging services.

    Non-server client (Browser/Mobile)

    When initializing the client in a client-side environment, pass the logger function within the options object:

    const client = StreamChat.getInstance('api_key', {
      logger: (logLevel, message, extraData) => {
        console.log(message); 
      },
    });

    Server side client (Node.js)

    When initializing the client on a server, you must provide the secret as the second argument, and the logger function is passed within the options object:

    const client = StreamChat.getInstance(
      'api_key',
      'secret',
      {
        logger: (logLevel, message, extraData) => {
          console.log(message);
        }
      }
    );
    const client = StreamChat.getInstance('api_key', {
      logger: (logLevel, message, extraData) => {
        console.log(message);
      },
    });
  8. Use the Burst Simulator to stress test chat UIs

    master

    Once the extension is loaded, follow these steps to run a performance test:

    1. Navigate to a page where a Stream Chat Channel is mounted.
    2. Click the Burst Simulator icon in your browser toolbar. The status should show Ready · <cid> · ….
    3. Configure the following parameters:
      • Total events: Number of synthetic events to dispatch (default 1000).
      • Rate per second: Pacing of events; set to 0 to fire all events in a single tick (useful for testing single long tasks).
      • Reactions ratio: A value between 0 and 1 representing the fraction of events that are reactions (default 0.25).
      • User pool size: Number of distinct fake users to rotate through (default 10).
    4. Click Fire burst. The status line will show live progress (e.g., Running · 230 / 1000 · 76 eps · 3s).
    5. To abort a run, click the red Stop button.

    Note: Closing the popup does not stop the simulation; it merely detaches the UI. Reopening the popup will re-attach to the running process.

  9. Upload files in the Browser

    master

    In the browser, connect a user using client.connectUser and then use channel.sendFile to upload files from an input element. The sendFile method returns a response object containing the file URL.

    <input id="input" type="file" />
    <script src="https://cdn.jsdelivr.net/npm/stream-chat"></script>
    <script>
      const chatClient = StreamChat.getInstance('YOUR_API_KEY');
      chatClient.connectUser({ id: 'user_id' }, 'USER_TOKEN');
      const channel = chatClient.channel('messaging', 'channel_id', { members: ['user_id'] });
      
      const handleFiles = (e) => {
        channel.sendFile(e.target.files[0]).then((file) => {
          console.log('File uploaded to:', file.file);
        });
      };
    
      document.getElementById('input').addEventListener('change', handleFiles, false);
    </script>
  10. Get started with StreamChat client

    master

    Initialize a new StreamChat client using your API Key and API Secret. For server-side (Node.js) use, it is recommended to set disableCache: true to avoid issues with local caching in a backend environment.

    Basic workflow:

    1. Initialize the client.
    2. Upsert a user using upsertUser.
    3. Create a channel using client.channel(type, id, data) and call channel.create().
    4. Send messages with channel.sendMessage({ text: '...' }).
    5. Send reactions with channel.sendReaction(messageId, { type, user }).
    import { StreamChat } from 'stream-chat';
    
    const client = new StreamChat('API_KEY', 'API_SECRET', {
      disableCache: true, // recommended option for server-side use
    });
    
    // create a user
    await client.upsertUser({
      id: 'vishal-1',
      name: 'Vishal',
    });
    
    // create a channel
    const channel = client.channel('messaging', 'test-channel', {
      created_by_id: 'vishal-1',
    });
    await channel.create();
    
    // send message
    const { message } = await channel.sendMessage({ text: 'This is a test message' });
    
    // send reaction
    await channel.sendReaction(message.id, { type: 'love', user: { id: 'vishal-1' } });
  11. Connect to Stream Chat React Native SDK during development

    master

    If you are developing the stream-chat-react-native SDK and want to use the local stream-chat-js repository, follow these steps:

    1. In the root of stream-chat-js, run: yarn link.
    2. In the root of your React Native example app, run: yarn link stream-chat and yarn start.
    3. Update your metro.config.js to include the stream-chat-js path in watchFolders and extraNodeModules so Metro can resolve the linked package.
    // metro.config.js
    const streamChatRoot = '<PATH TO YOUR PROJECT>/stream-chat-js'
    
    module.exports = {
      ... 
      watchFolders: [projectRoot].concat(alternateRoots).concat([streamChatRoot]),
      resolver: {
        ... 
        extraNodeModules: {
          ... 
          'stream-chat': streamChatRoot
        }
      }
    };