Skip to main content
Version: v5

Server-Sent Events

Added in: v4.2.0

Harper supports Server-Sent Events (SSE), a simple and efficient mechanism for browser-based applications to receive real-time updates from the server over a standard HTTP connection. SSE is a one-directional transport — the server pushes events to the client, and the client has no way to send messages back on the same connection.

Connecting

SSE connections are made by targeting a resource URL. By default, connecting to a resource path subscribes to changes for that resource and streams events as they occur.

let eventSource = new EventSource('https://server/my-resource/341', {
withCredentials: true,
});

eventSource.onmessage = (event) => {
let data = JSON.parse(event.data);
};

The URL path maps to the resource in the same way as REST and WebSocket connections. Connecting to /my-resource/341 subscribes to updates for the record with id 341 in the my-resource table (or custom resource).

connect() Handler

SSE connections use the same connect() method as WebSockets on resource classes, with one key difference: since SSE is one-directional, connect() is called without an incomingMessages argument.

export class MyResource extends Resource {
async *connect() {
// yield messages to send to the client
while (true) {
await someCondition();
yield { event: 'update', data: { value: 42 } };
}
}
}

The default connect() behavior subscribes to the resource and streams changes automatically.

Reading the request in connect()

To read the incoming request (query parameters, headers, the path), define connect() as a static method. Its first argument is the RequestTarget, which exposes the query string via target.get():

export class Search extends Resource {
static connect(target) {
const query = target.get('q'); // GET /Search?q=harper
const apiKey = target.get('api_key');
return this.stream(query, apiKey); // return the generator that streams
}
static async *stream(query, apiKey) {
for (const hit of await runSearch(query)) yield { data: hit };
}
}

connect() itself is not the generator. It reads what it needs from target synchronously, then returns the async * generator that yields events. You can also return an inline generator: return (async function* () { … })().

Note: the RequestTarget argument is only passed to a static connect(). An instance method's first argument is not the target, so define connect() (and any delegated generator) as static.

When to Use SSE vs WebSockets

SSEWebSockets
DirectionServer → Client onlyBidirectional
TransportStandard HTTPHTTP upgrade
Browser supportNative EventSource APINative WebSocket API
Use caseLive feeds, dashboards, notificationsInteractive real-time apps, MQTT

SSE is simpler to implement and has built-in reconnection in browsers. For scenarios requiring bidirectional communication, use WebSockets.

See Also