Implementing Server‑Sent Events (SSE) with NestJS and React
This tutorial explains the concept of Server‑Sent Events, compares it with WebSocket, and provides a step‑by‑step guide to create an SSE endpoint in a NestJS backend, enable CORS, and consume the stream from a React frontend using the native EventSource API.
When thinking about pushing data from server to client, WebSocket often comes to mind because it supports full‑duplex communication, but for one‑way push scenarios the HTTP‑based Server‑Sent Events (SSE) is a simpler alternative.
SSE works by returning a response with Content‑Type: text/event-stream , which keeps the connection open and allows the server to send multiple JSON messages over time.
To try it yourself, first create a NestJS project:
npx nest new sse-testStart the development server:
npm run start:devAdd an SSE endpoint in AppController using the @Sse decorator. The method returns an Observable that calls observer.next whenever a new message should be sent.
@Sse('stream')
stream() {
return new Observable(observer => {
observer.next({ data: { msg: 'aaa' } });
setTimeout(() => {
observer.next({ data: { msg: 'bbb' } });
}, 2000);
setTimeout(() => {
observer.next({ data: { msg: 'ccc' } });
}, 5000);
});
}On the frontend, create a React TypeScript project and use the native EventSource API to connect to the SSE endpoint. The onmessage handler parses the incoming JSON and logs it.
import { useEffect } from 'react';
function App() {
useEffect(() => {
const eventSource = new EventSource('http://localhost:3000/stream');
eventSource.onmessage = ({ data }) => {
console.log('New message', JSON.parse(data));
};
}, []);
return
hello
;
}
export default App;Enable CORS in the NestJS application so the browser can access the endpoint from a different origin.
SSE also works well for streaming logs or ChatGPT‑style incremental responses. By executing tail -f via child_process.exec and forwarding the stdout data through an SSE stream, you can display real‑time log updates in the browser.
const { exec } = require('child_process');
const childProcess = exec('tail -f ./log');
return new Observable(observer => {
childProcess.stdout.on('data', msg => {
observer.next({ data: { msg } });
});
});Binary data can also be sent: read a file into a Buffer , call toJSON() to obtain a JSON‑serializable representation, and push it through SSE.
const { readFileSync } = require('fs');
const json = readFileSync('./package.json').toJSON();
observer.next({ data: { msg: json } });Compatibility is good: all modern browsers except IE support SSE, and the browser automatically reconnects if the connection drops, unlike WebSocket which requires manual reconnection.
Typical use cases include internal notifications, real‑time build logs, and incremental AI responses, making SSE a lightweight alternative to WebSocket for one‑directional server push scenarios.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.