Mock server-sent events (SSE) with msw

Posted 5 April 2024


Server-sent events allow streaming events from a server to a frontend application. They are similar to websockets but provide only a one-way connection from server to client.

Generative AI solutions like ChatGPT make use of server-sent events to stream the large language model response to the client as it's generated. With the popularity of generative AI applications, figuring out how to work with these streamed responses is one challenge to overcome with some differences to a typical REST API.

Mock Service Worker is a popular tool for writing JavaScript API mocks that can be re-used across different frameworks, tools and environments. At Mintel we use msw to write mocks to use in our Storybook, unit tests and other development environments.

msw does support mocking SSE events, while websocket support is in beta. For example, given some frontend code that opens a server-sent events connection:

const source = new EventSource("http://example.com/stream");

source.onmessage = (event) => {
  console.log(`Message: ${event.data}`);
};

You can write an msw mock handler:

import { http, HttpResponse } from "msw";

const encoder = new TextEncoder();

const handler = http.get('http://example.com/stream', () => {
  const stream = new ReadableStream({
    start(controller) {
      controller.enqueue(
        encoder(
          `event:some-event\ndata:some data\n\n`
        )
      );
      controller.close();
    },
  });

  return new HttpResponse(stream, {
    headers: {
      "Content-Type": "text/event-stream",
    }.
  });
});

This mock will respond with a single some-event event that contains "some data" in event.data. Each message should contain lines of text that represent each field and value, with the field name and value separated by a colon. Messages are separated by a pair of newline characters. You can read more about the event stream message format.

You can also read more about streaming with msw with examples of how you can pipe your response through a latency stream to replicate a delayed response.

If you're hitting some limitations of the EventSource API, such as not being able to provide custom request headers for authentication you can also check out fetch-event-source as this mocking approach will work with both.


Related posts

Contract testing with OpenAPI & TypeScript

Published

Validate contracts between backend services and frontend applications

Platform team challenges

Published

Challenges faced when introducing a platform team

Accessibility testing

Published

Overview of approaches and tools for testing web accessibility


Thanks for reading

I'm Alex O'Callaghan and this is my personal website where I write about software development and do my best to learn in public. I currently work at Mintel as a Principal Engineer working primarily with React, TypeScript & Python.

I've been leading one of our platform teams, first as an Engineering Manager and now as a Principal Engineer, maintaining a collection of shared libraries, services and a micro-frontend architecture.