Background

In @learning-labs, I organized a focused SSE (Server-Sent Events) practice track to connect the dots between concepts, implementation, and testing, while clarifying when SSE is a better fit than WebSocket.

The materials live in learning-labs/sse/, source:
https://github.com/wfnking/learning-labs/tree/master/sse

Including:

  • go-example/: a complete Go SSE server + browser test page
  • node-example/: Node.js CLI SSE clients
  • README.md: concepts, formats, implementation notes, and use cases

Core SSE Traits (Practice View)

  1. Server-to-client only: ideal for notifications, logs, and progress updates
  2. HTTP-based: Content-Type: text/event-stream, no extra protocol
  3. Auto-reconnect: EventSource handles retries with retry and Last-Event-ID
  4. Named events: use the event: field to classify streams

If you only need server push and no bidirectional messaging, SSE is usually simpler and easier to operate.


Server Implementation Essentials

1) Required response headers

w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")

2) Standardize message formatting

func formatSSEMessage(event, data string) string {
    if event != "" {
        return fmt.Sprintf("event: %s\ndata: %s\n\n", event, data)
    }
    return fmt.Sprintf("data: %s\n\n", data)
}

3) Keep the connection open + flush

With long-lived SSE connections:

  • keep the connection alive
  • flush on each write
  • clean up on client disconnect

4) Hub pattern for concurrency

The learning project uses a minimal hub to avoid blocking:

type SSEHub struct {
    clients    map[string]*SSEClient
    register   chan *SSEClient
    unregister chan *SSEClient
    broadcast  chan string
}

Practice Examples (learning-labs/sse)

Go endpoints

  • /events: basic broadcast
  • /clock: push time every second
  • /stock: simulated stock updates (JSON)

Node clients

CLI scripts for validating different streams:

  • basic.js: baseline connection + messages
  • clock.js: real-time clock feed
  • stock.js: JSON parsing and display

SSE vs WebSocket: My Rule of Thumb

  • Server push only → choose SSE
  • Bidirectional or binary → choose WebSocket

SSE wins on simplicity and native browser support for lightweight real-time feeds.


Common Pitfalls (from practice)

  1. Proxy buffering: intermediaries may buffer responses and delay events
  2. Browser connection limits: ~6 SSE connections per host on HTTP/1.1
  3. Missing flush: no immediate delivery without Flush()
  4. Leaked clients: forget to remove disconnected clients → memory leaks

Quick Start

cd learning-labs/sse/go-example
go run main.go

Open: http://localhost:8888
Or test with Node clients:

cd learning-labs/sse/node-example
npm install
npm run basic

Closing

This SSE practice in @learning-labs is meant to be a minimal, runnable knowledge sample:
it preserves protocol details while keeping the path to working examples short.

If you are building a real-time system, SSE is a strong default for lightweight server push.