Skip to main content

Server & Client Patterns, Event Catalog

Push live updates without polling; keep payloads small and stable.

C
Written by Catalin Fetean
Updated over 2 weeks ago

Audience: Frontend + Backend Engineers
Outcomes: Reliable real-time UX; versionable event contracts

Server (Node)

const clients = new Set<import('http').ServerResponse>(); app.get('/api/events/stream', (req, res) => { res.set({ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); res.flushHeaders(); clients.add(res); req.on('close', () => clients.delete(res)); }); function emitEvent(name: string, data: any) { const payload = `event: ${name}\ndata: ${JSON.stringify(data)}\n\n`; for (const res of clients) if (!res.destroyed) res.write(payload); }

Client (browser)

const es = new EventSource('/api/events/stream', { withCredentials: true }); ['order.status.changed','payment.succeeded','dispute.opened'].forEach(name => { es.addEventListener(name, e => console.log(name, JSON.parse(e.data))); });

Event catalog (selection)

Event

Payload (short)

order.created

{orderId, title, total, currency}

order.status.changed

{orderId, from, to}

deliverable.uploaded

{orderId, deliverableId, milestoneId}

deliverable.accepted

{orderId, deliverableId}

deliverable.rejected

{orderId, deliverableId, reason}

payment.succeeded

{orderId, amount, currency, providerRef}

payment.failed

{orderId, errorCode}

escrow.released

{orderId, milestoneId, amount}

dispute.opened

{orderId, disputeId, reason}

dispute.resolved

{orderId, disputeId, resolution}

contract.fully_signed

{contractId, number}

Enrichment & filtering

  • Subscribe only to needed names; stream is org-scoped

  • Enrich server-side with tiny derived fields (e.g., displayAmount)

  • Anti-pattern: dumping whole objects on every event

Change policy

  • Additive changes (new fields) are safe

  • Breaking changes → new event name or versioned channel

QA checklist

  • CORS allows your origin; proxies don’t buffer SSE

  • Auto-reconnect works; handlers are idempotent

Runbook: “SSE outage”

  • Disable proxy buffering; check TLS/CORS; confirm withCredentials:true.

Did this answer your question?