From Zero to AI

Lesson 2.2: Server-Sent Events

Duration: 60 minutes

Learning Objectives

By the end of this lesson, you will be able to:

  1. Explain what Server-Sent Events (SSE) are and how they work
  2. Understand the SSE protocol and message format
  3. Compare SSE with other real-time technologies
  4. Parse and handle SSE streams in TypeScript

What Are Server-Sent Events?

Server-Sent Events (SSE) is a web standard that allows servers to push data to clients over HTTP. Unlike traditional request-response patterns where the client asks and the server answers once, SSE keeps the connection open and sends multiple messages over time.

Think of it like a radio broadcast: you tune in once, and the station keeps sending you content until you disconnect.


How SSE Works

The SSE flow is simple:

  1. Client opens an HTTP connection to the server
  2. Server keeps the connection open
  3. Server sends messages as events occur
  4. Client receives and processes each message
  5. Connection stays open until client or server closes it
Client                    Server
  |                         |
  |------- GET /stream ---->|
  |                         |
  |<------ data: Hello -----|
  |<------ data: World -----|
  |<------ data: [DONE] ----|
  |                         |

SSE Message Format

SSE messages follow a specific text format. Each message consists of fields separated by newlines:

data: This is a message

data: This is another message

event: custom-event
data: {"key": "value"}

Key fields in SSE:

  • data: The actual message content (required)
  • event: Optional event type name
  • id: Optional message identifier
  • retry: Suggested reconnection time in milliseconds

Messages are separated by blank lines (two newlines).


SSE in AI APIs

Both OpenAI and Anthropic use SSE to stream responses. When you enable streaming, the API sends chunks like this:

data: {"id":"chatcmpl-123","choices":[{"delta":{"content":"Hello"}}]}

data: {"id":"chatcmpl-123","choices":[{"delta":{"content":" world"}}]}

data: [DONE]

Each data: line contains a JSON object with the next piece of the response. The [DONE] marker signals the stream is complete.


Parsing SSE Manually

Understanding the raw format helps with debugging. Here is a basic SSE parser:

function parseSSE(rawData: string): string[] {
  const messages: string[] = [];
  const lines = rawData.split("
");

  for (const line of lines) {
    if (line.startsWith("data: ")) {
      const data = line.slice(6); // Remove "data: " prefix
      if (data !== "[DONE]") {
        messages.push(data);
      }
    }
  }

  return messages;
}

// Example usage
const raw = `data: {"content":"Hello"}

data: {"content":" World"}

data: [DONE]`;

const messages = parseSSE(raw);
// ["{"content":"Hello"}", "{"content":" World"}"]

In practice, you will use the SDK which handles parsing automatically.


SSE vs Other Technologies

Technology Direction Connection Use Case
SSE Server to Client Long-lived Streaming AI responses
WebSockets Bidirectional Long-lived Chat apps, games
HTTP Polling Client to Server Repeated Legacy systems
Long Polling Both Repeated Fallback option

SSE is ideal for AI streaming because:

  • Responses flow one direction (server to client)
  • Built on standard HTTP (works everywhere)
  • Automatic reconnection support
  • Simpler than WebSockets

Handling SSE in Node.js

When working with SSE streams in Node.js, you typically use async iterators:

async function processStream(response: Response): Promise<string> {
  const reader = response.body?.getReader();
  if (!reader) throw new Error("No response body");

  const decoder = new TextDecoder();
  let result = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split("
");

    for (const line of lines) {
      if (line.startsWith("data: ") && line !== "data: [DONE]") {
        const json = JSON.parse(line.slice(6));
        const content = json.choices?.[0]?.delta?.content ?? "";
        result += content;
        process.stdout.write(content);
      }
    }
  }

  return result;
}

The SDK abstracts this complexity, but understanding the underlying mechanism helps with debugging.


Error Handling in SSE

SSE connections can fail for various reasons:

async function streamWithRetry(url: string, maxRetries: number = 3): Promise<void> {
  let attempts = 0;

  while (attempts < maxRetries) {
    try {
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      await processStream(response);
      return; // Success
    } catch (error) {
      attempts++;
      console.error(`Attempt ${attempts} failed:`, error);

      if (attempts < maxRetries) {
        const delay = Math.pow(2, attempts) * 1000;
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
  }

  throw new Error(`Failed after ${maxRetries} attempts`);
}

Common SSE errors:

  • Network disconnection
  • Server timeout
  • Rate limiting (429 status)
  • Invalid response format

Browser vs Node.js

SSE is a web standard with native browser support via EventSource:

// Browser-only
const source = new EventSource('/api/stream');

source.onmessage = (event) => {
  console.log('Received:', event.data);
};

source.onerror = (error) => {
  console.error('SSE error:', error);
};

In Node.js, there is no native EventSource, so we use:

  • The SDKs native streaming support
  • Fetch API with manual parsing
  • Third-party libraries like eventsource

Key Takeaways

  1. SSE is a web standard for server-to-client streaming over HTTP
  2. Messages use a simple text format with data: prefixes
  3. AI APIs use SSE to stream token-by-token responses
  4. The SDK handles parsing but understanding the format helps debugging
  5. SSE is simpler than WebSockets for one-way streaming

Resources

Resource Type Level
MDN Server-Sent Events Documentation Beginner
SSE Specification Specification Advanced
OpenAI Streaming Format Documentation Beginner

Next Lesson

Now that you understand how SSE works, you are ready to implement streaming with real AI APIs. In the next lesson, you will build streaming responses with OpenAI.

Continue to Lesson 2.3: Streaming with OpenAI