0Pricing
NestJS Enterprise Backend APIs · Ders

Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar

@Sse dekoratörü ve RxJS gözlemlenebilirleriyle istemcilere canlı güncellemeler akıtın.

Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar, CoddyKit'te ücretsiz bir NestJS Enterprise Backend APIs dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, NestJS Enterprise Backend APIs öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. NestJS Enterprise Backend APIs kursu toplamda 4 dersten oluşur.

Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.

Why Server-Sent Events?

Server-Sent Events (SSE) let a server push a continuous stream of updates to a client over a single, long-lived HTTP connection. It is the simplest way to deliver one-way, server-to-client live data such as notifications, progress bars, or dashboard metrics.

  • One-way only: the server talks, the client listens. There is no client-to-server channel on the same connection.
  • Plain HTTP: no special protocol upgrade like WebSockets need. It rides on a normal GET request.
  • Auto-reconnect: the browser's EventSource reconnects automatically if the connection drops.

In NestJS, SSE is a first-class feature exposed through the @Sse() decorator combined with RxJS observables.

SSE vs WebSockets

Both stream live data, but they solve different problems. Choosing the right one is an architectural decision.

  • SSE: server → client only, text-based, runs over HTTP/1.1 or HTTP/2, built-in reconnection and event IDs. Ideal for feeds, alerts, and progress.
  • WebSockets: full-duplex (both directions), binary or text, requires a protocol upgrade. Ideal for chat, multiplayer games, and collaborative editing.

If your clients only need to receive updates, SSE is lighter, easier to scale behind standard HTTP infrastructure, and requires no extra client library. Reach for WebSockets only when the client must also push messages in real time.

The wire format

SSE is just a streaming HTTP response with the content type text/event-stream. Each message is a block of text fields separated by newlines, and each block ends with a blank line.

  • data: the payload (often a JSON string).
  • event: a named event type the client can listen for.
  • id: a message identifier used for reconnection via Last-Event-ID.
  • retry: the reconnection delay in milliseconds.

You rarely format this by hand in NestJS — the framework serializes a typed object into these fields for you — but knowing the shape helps you debug with curl.

// Raw text/event-stream bytes the server emits
// (NestJS builds this for you from a MessageEvent)
const sample =
  'id: 42\n' +
  'event: heartbeat\n' +
  'data: {"status":"ok","ts":1718000000}\n' +
  '\n';

process.stdout.write(sample);

Your first @Sse endpoint

The @Sse() decorator marks a controller method as an SSE stream. Instead of returning a plain value, the method returns an RxJS Observable. Every value the observable emits becomes one SSE message sent to the client.

NestJS expects each emitted value to be a MessageEvent-shaped object with a data property. It automatically sets the text/event-stream headers and keeps the connection open.

import { Controller, Sse, MessageEvent } from '@nestjs/common';
import { interval, map, Observable } from 'rxjs';

@Controller('events')
export class EventsController {
  @Sse('clock')
  clock(): Observable<MessageEvent> {
    return interval(1000).pipe(
      map((n) => ({ data: { tick: n, time: new Date().toISOString() } })),
    );
  }
}

The MessageEvent shape

NestJS exports a MessageEvent interface that maps directly onto the SSE wire fields. Only data is required; the rest are optional.

  • data — string or object. Objects are JSON-stringified automatically.
  • type — becomes the event: field (a named event).
  • id — becomes the id: field, enabling resume-on-reconnect.
  • retry — becomes the retry: field in milliseconds.

Returning a well-typed object keeps your stream self-documenting and lets clients subscribe to specific named events.

import { MessageEvent } from '@nestjs/common';

function buildEvent(orderId: string, n: number): MessageEvent {
  return {
    id: String(n),
    type: 'order.updated',
    retry: 5000,
    data: { orderId, sequence: n },
  };
}

console.log(buildEvent('ord_123', 7));

Pushing domain events with a Subject

A fixed interval is fine for clocks, but real systems push when something happens. The idiomatic pattern is an RxJS Subject living in a service. Your business logic calls .next() on the subject whenever an event occurs, and the SSE endpoint simply exposes the subject as an observable.

This cleanly decouples the producer (any service) from the transport (the SSE controller).

import { Injectable, MessageEvent } from '@nestjs/common';
import { Subject, Observable } from 'rxjs';

@Injectable()
export class NotificationsService {
  private readonly stream$ = new Subject<MessageEvent>();

  emit(payload: unknown): void {
    this.stream$.next({ type: 'notification', data: payload });
  }

  asObservable(): Observable<MessageEvent> {
    return this.stream$.asObservable();
  }
}

Wiring the service to the controller

The controller injects the service and returns its observable from an @Sse() method. Any other part of the app — a queue consumer, a webhook handler, a cron job — can inject the same service and call emit() to broadcast to every connected client.

Because a plain Subject is multicast, all subscribers receive each emission. This is exactly what you want for a shared notification feed.

import { Controller, Post, Body, Sse, MessageEvent } from '@nestjs/common';
import { Observable } from 'rxjs';
import { NotificationsService } from './notifications.service';

@Controller('notifications')
export class NotificationsController {
  constructor(private readonly notifications: NotificationsService) {}

  @Sse('stream')
  stream(): Observable<MessageEvent> {
    return this.notifications.asObservable();
  }

  @Post()
  publish(@Body() body: { message: string }) {
    this.notifications.emit(body);
    return { accepted: true };
  }
}

Per-user filtered streams

A global subject broadcasts to everyone. In an enterprise API you usually want each client to receive only their events. Use RxJS operators like filter and map to tailor the stream per request, reading the user from a route param or the authenticated request.

The @Sse() method can accept normal route decorators such as @Param() and @Req(), so you can scope the observable to the current user.

import { Controller, Param, Sse, MessageEvent } from '@nestjs/common';
import { Observable, filter, map } from 'rxjs';
import { EventsBus } from './events.bus';

@Controller('users')
export class UserFeedController {
  constructor(private readonly bus: EventsBus) {}

  @Sse(':userId/feed')
  feed(@Param('userId') userId: string): Observable<MessageEvent> {
    return this.bus.events$.pipe(
      filter((e) => e.userId === userId),
      map((e) => ({ type: e.kind, data: e.payload })),
    );
  }
}

Heartbeats keep the connection alive

Proxies, load balancers, and browsers may close an idle connection. A heartbeat — a periodic comment or no-op event — keeps the pipe warm. With RxJS you merge your real event stream with a slow timer.

Send heartbeats as a distinct event type (or as SSE comment lines) so clients can ignore them. A common interval is every 15–30 seconds, comfortably under typical proxy idle timeouts.

import { merge, interval, map, Observable } from 'rxjs';
import { MessageEvent } from '@nestjs/common';

export function withHeartbeat(
  source$: Observable<MessageEvent>,
): Observable<MessageEvent> {
  const heartbeat$ = interval(15000).pipe(
    map((): MessageEvent => ({ type: 'heartbeat', data: 'ping' })),
  );
  return merge(source$, heartbeat$);
}

Cleanup, errors, and backpressure

When a client disconnects, NestJS unsubscribes from your observable. Make sure your stream releases resources on unsubscribe — use finalize() for cleanup and never leak timers or listeners.

  • Use catchError to convert errors into a final event instead of crashing the stream.
  • Use finalize to log or decrement a connection counter when the client leaves.
  • Beware backpressure: a fast producer with a slow client buffers in memory. Throttle or sample high-frequency sources.
import { Observable, catchError, finalize, of } from 'rxjs';
import { MessageEvent } from '@nestjs/common';

export function safeStream(
  source$: Observable<MessageEvent>,
  onClose: () => void,
): Observable<MessageEvent> {
  return source$.pipe(
    catchError((err) =>
      of<MessageEvent>({ type: 'error', data: { message: err.message } }),
    ),
    finalize(onClose),
  );
}

Consuming the stream from a client

Browsers consume SSE with the native EventSource API. It connects, dispatches messages, and reconnects automatically. Listen to the default message event for unnamed data, or add listeners for your named event types.

Note that EventSource only supports GET and cannot set custom headers, so auth is usually done via cookies or a token in the query string. Tools like curl -N are great for quick debugging from the terminal.

// Browser-side consumer
const es = new EventSource('/notifications/stream');

es.addEventListener('notification', (e) => {
  const payload = JSON.parse(e.data);
  console.log('new notification', payload);
});

es.addEventListener('heartbeat', () => {
  // keep-alive, ignore
});

es.onerror = () => console.warn('reconnecting...');

Quick Check

Test your understanding of when and how to use SSE in NestJS.

Recap

You learned how to stream one-way live updates from NestJS using Server-Sent Events:

  • SSE is server-to-client only over plain HTTP, with built-in browser auto-reconnect — choose it over WebSockets when clients only need to receive.
  • The @Sse() decorator turns a controller method into a stream that returns an Observable<MessageEvent>; each emission becomes one message.
  • A MessageEvent maps to the wire fields data, type, id, and retry; objects are JSON-serialized for you.
  • Push real domain events with a shared Subject in a service, and scope per-user streams with filter and map.
  • Keep connections healthy with heartbeats via merge, and clean up safely using catchError and finalize.
  • Consume on the client with the native EventSource API.

Sıkça Sorulan Sorular

“Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar” dersi ücretsiz mi?

Evet — “Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve NestJS Enterprise Backend APIs kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. NestJS Enterprise Backend APIs kursu toplamda 4 dersten oluşur.

“Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar” dersinde ne öğreneceğim?

@Sse dekoratörü ve RxJS gözlemlenebilirleriyle istemcilere canlı güncellemeler akıtın. NestJS Enterprise Backend APIs ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

NestJS Enterprise Backend APIs öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te NestJS Enterprise Backend APIs, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu NestJS Enterprise Backend APIs dersinde kod yazıp çalıştırabilir miyim?

Evet. Her NestJS Enterprise Backend APIs dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Socket.IO Adaptörüyle WebSocket Ağ Geçitleri
  2. Socket Bağlantılarının Kimliğini Doğrulama ve Koruma
  3. Tek Yönlü İletim için Sunucu Tarafından Gönderilen Olaylar
  4. Redis Pub/Sub Adaptörüyle Gerçek Zamanlı Ölçekleme
← NestJS Enterprise Backend APIs Sayfasına Dön