Pick one mechanism per flow — don't confuse SSE with WebSocket
Use SSE for one-way LLM token streaming — reserve WebSocket for bidirectional chat.
nestjs-realtime-transport-sse-vs-websocket
Why it matters
| Stake | If ignored |
|---|---|
| Can't scale |
|
How to fix
- SSE (Server-Sent Events) — one-way stream from server to client. Ideal for LLM token streaming, job progress, notifications.
- WebSocket — bidirectional. Required only when the client sends frequent unsolicited messages (collaborative editing, user-to-user chat).
- Long polling — fallback, not default.
Examples
ts
@WebSocketGateway()
export class ChatGateway {
@SubscribeMessage('prompt')
async handlePrompt(@MessageBody() data: any, @ConnectedSocket() client: Socket) {
for await (const token of this.llm.stream(data.prompt)) {
client.emit('token', token);
}
}
}ts
@Controller('chat')
export class ChatController {
@Post('stream')
@Sse()
async stream(@Body() dto: PromptDto): Promise<Observable<MessageEvent>> {
return new Observable((subscriber) => {
(async () => {
for await (const token of this.llm.stream(dto.prompt)) {
subscriber.next({ data: { token } });
}
subscriber.complete();
})();
});
}
}