Aller au contenu

SSE

Les Server-Sent Events (SSE) permettent de pousser des données du serveur vers le client sur une connexion HTTP persistante. Lacis a un support SSE natif via res.initSSE().

// routes/events/index.ts
import type { Request, Response } from 'lacis'
export async function GET(req: Request, res: Response) {
const sse = res.initSSE()
// Envoyer des événements
sse.json({ type: 'connected' })
await new Promise(resolve => setTimeout(resolve, 1000))
sse.json({ type: 'update', data: { value: 42 } })
sse.close()
}

initSSE() définit les headers de réponse (Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive) et retourne un SSEContext.

MéthodeDescription
send(data: string)Envoie une ligne data: brute
json(data: any)Envoie des données sérialisées en JSON
event(event: string, data: any)Envoie un événement nommé avec des données JSON
comment(text: string)Envoie un commentaire (utile comme keepalive)
id(id: string)Définit le champ id de l’événement
retry(ms: number)Indique au client le délai d’attente avant reconnexion
close(comment?: string)Ferme la connexion gracieusement
error(event, message, code?, details?)Envoie un événement d’erreur et ferme

Toutes les méthodes retournent false si la connexion est déjà fermée.

const sse = res.initSSE({
timeout: 300_000, // ms avant fermeture automatique (défaut : 300000 = 5 min)
headers: {
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no', // désactive le buffering Nginx
},
})
// ✅ Correct — initSSE avant tout await
export async function GET(req: Request, res: Response) {
const sse = res.initSSE()
const data = await fetchSomething()
sse.json(data)
sse.close()
}
// ❌ Incorrect sur Bun — await avant initSSE
export async function GET(req: Request, res: Response) {
const data = await fetchSomething()
const sse = res.initSSE() // lève une erreur sur Bun
}

Cette contrainte ne s’applique pas à Node.js.

// routes/chat/index.ts
import type { Request, Response } from 'lacis'
export async function POST(req: Request, res: Response) {
const sse = res.initSSE() // doit être avant tout await sur Bun
const { prompt } = await req.json<{ prompt: string }>()
try {
const stream = await llm.stream(prompt)
for await (const chunk of stream) {
sse.json({ delta: chunk.text })
}
sse.event('done', { finish_reason: 'stop' })
sse.close()
} catch (err) {
sse.error('error', 'Échec du stream', 500)
}
}
import { createSSEClient } from 'lacis'
const client = await createSSEClient(url, options, handlers)

Options :

OptionTypeDéfautDescription
methodstring'GET' (ou 'POST' si body défini)Méthode HTTP
bodystringCorps de la requête
contentTypestring'application/json' si bodyHeader Content-Type
reconnectIntervalnumber3000Ms entre les tentatives de reconnexion
maxRetriesnumber3Nombre max de tentatives de reconnexion
disableReconnectbooleanfalseDésactive la reconnexion automatique

Handlers :

HandlerDescription
onMessage(data)Reçoit les événements data: génériques
onEventRecord<string, (data) => void> pour les événements nommés
onClose()Appelé quand la connexion se ferme
onError(error)Appelé en cas d’erreur de connexion