ng-sse-client 1.0.0
Install from the command line:
Learn more about npm packages
$ npm install @spectrayan/ng-sse-client@1.0.0
Install via package.json:
"@spectrayan/ng-sse-client": "1.0.0"
About this version
A small, typed, zone-aware Server‑Sent Events (SSE) client for Angular.
- Typed APIs:
stream<T>(url)andstreamEvent<T>(url, event) - Auto‑reconnect with exponential backoff + jitter
- Handles default
messageand custom named events - Adds
?lastEventId=...on reconnect to resume from the last received event - Zone-friendly: network/parsing outside Angular, emissions re-enter the zone
- Optional lifecycle hooks (onConnect, onOpen, onMessage, onError, onReconnectAttempt, onClose)
- Optional event-driven API callbacks with retry (e.g., mark notifications as read)
npm install @spectrayan/ng-sse-clientPeer dependencies
- Angular >=16 and <21 (incl. 20)
- RxJS 7
import { Component } from '@angular/core';
import { SseClient } from '@spectrayan/ng-sse-client';
@Component({
selector: 'app-demo',
template: `<ul><li *ngFor="let m of messages">{{ m }}</li></ul>`
})
export class DemoComponent {
messages: string[] = [];
constructor(sse: SseClient) {
sse.stream<string>('http://localhost:8080/sse/general').subscribe(m => {
this.messages.unshift(m);
});
}
}Named events
sse
.streamEvent<{ id: string; text: string }>('http://localhost:8080/sse/notifications', 'notification')
.subscribe(n => console.log(n.id, n.text));Typed parsing (safe JSON-or-string)
const parse = <T>(txt: string): T => {
try { return JSON.parse(txt) as T; } catch { return txt as unknown as T; }
};
sse.stream<any>('http://localhost:8080/sse/general', { parse }).subscribe(v => console.log(v));See samples/ng-sse-client-app/src/app/app.ts for a complete demo. Summarized usage:
import { ApiCallbackConfig, SseClient } from '@spectrayan/ng-sse-client';
// Configure a server-side side-effect to run when a notification arrives
const markReadCallback: ApiCallbackConfig<{ id: string }> = {
method: 'POST',
url: 'http://localhost:8080/api/notifications/mark-read',
transformPayload: (event) => ({ notificationId: event.id }),
headers: { 'Content-Type': 'application/json' },
timeout: 5000,
};
sse.stream<any>(`http://localhost:8080/sse/john`, {
events: ['notification'],
parse: (txt) => { try { return JSON.parse(txt); } catch { return txt; } },
reconnection: { enabled: true, maxRetries: -1, initialDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2, jitterRatio: 0.2 },
callbacks: [
{
eventType: 'notification',
condition: (d: any) => !!(d && typeof d === 'object' && 'id' in d),
apiCallback: markReadCallback,
retry: { enabled: true, maxRetries: 3, delayMs: 1000 },
},
],
}).subscribe(/* ... */);- The client collects
messageandnotificationevents, parses payloads, and updates the UI. - When a
notificationarrives, it triggers a POST to mark it as read. The callback can retry without failing the SSE stream.
sse.stream<any>('http://localhost:8080/sse/user', {
withCredentials: true,
events: ['notification'], // named events ("message" is implicit and handled by default)
lastEventIdParamName: 'lastEventId',
parse: (txt) => { try { return JSON.parse(txt); } catch { return txt; } },
reconnection: {
enabled: true,
maxRetries: -1, // -1 = infinite retries
initialDelayMs: 1000,
maxDelayMs: 30000,
backoffMultiplier: 2,
jitterRatio: 0.2,
},
callbacks: [ /* see Event callbacks section */ ],
});Use the helper provideSseClient(...) in your app.config.ts:
import { ApplicationConfig } from '@angular/core';
import { provideSseClient } from '@spectrayan/ng-sse-client';
export const appConfig: ApplicationConfig = {
providers: [
provideSseClient({
withCredentials: true,
lastEventIdParamName: 'lastEventId',
reconnection: { enabled: true, maxRetries: -1, initialDelayMs: 1000, maxDelayMs: 30000, backoffMultiplier: 2, jitterRatio: 0.2 },
}),
],
};Alternatively, you can still provide the token directly:
import { ApplicationConfig } from '@angular/core';
import { SSE_CLIENT_CONFIG } from '@spectrayan/ng-sse-client';
export const appConfig: ApplicationConfig = {
providers: [
{ provide: SSE_CLIENT_CONFIG, useValue: { withCredentials: true } },
],
};Values passed to stream()/streamEvent() override global defaults.
-
stream<T>(url: string, options?: StreamOptions<T>): Observable<T>- Subscribes to default
messageevents plus anyoptions.events(excluding duplicatemessage).
- Subscribes to default
-
streamEvent<T>(url: string, event: string, options?: StreamOptions<T>): Observable<T>- Subscribes only to the given named event.
Both return a cold Observable that opens an EventSource connection on subscribe and closes it on unsubscribe/complete/error.
StreamOptions<T> extends Partial<Omit<SseClientConfig, 'url' | 'parse'>> and adds:
-
parse?: (data: string) => T— parser for incoming payload. Defaults toJSON.parse(seeDEFAULT_SSE_CLIENT_CONFIG).
Practical fields you will commonly use:
withCredentials?: boolean-
events?: string[]— named events to attach listeners for (besides defaultmessage). -
lastEventIdParamName?: string— query param name for lastEventId on reconnect. reconnection?: SseReconnectionConfig-
callbacks?: EventCallbackConfig[]— see next section. -
hooks?: SseClientHooks— lifecycle hooks for connection and events.
You can trigger HTTP calls when events arrive — useful for acknowledgement flows, analytics, etc.
Types:
export interface ApiCallbackConfig<T = any> {
method: 'POST' | 'PUT' | 'PATCH';
url: string;
transformPayload?: (eventData: T) => any;
headers?: Record<string, string>;
withCredentials?: boolean;
timeout?: number; // ms
}
export interface EventCallbackConfig<T = any> {
eventType?: string; // if omitted, applies to all events
condition?: (eventData: T) => boolean;
apiCallback: ApiCallbackConfig<T>;
retry?: { enabled: boolean; maxRetries: number; delayMs: number };
}Behavior:
- For each event, the client checks all
callbacks. - If
eventTypeis set, it must match the current event. - If
conditionis provided, it must return true. - The HTTP request is executed via
ApiCallbackServiceoutside Angular zone. - If
retry.enabled, failed requests will be retried up tomaxRetrieswith fixed delaydelayMs. - Callback failures are logged and do not error the SSE stream.
You can observe connection lifecycle events via hooks, either globally (DI) or per stream.
Types:
export interface SseClientHooks {
onConnect?: (url: string) => void;
onOpen?: (info: { url: string; attempt: number }) => void;
onMessage?: (info: { eventType: string; data: any; rawEvent: MessageEvent }) => void;
onError?: (info: { event: Event; attempt: number; willRetry: boolean; nextDelayMs?: number }) => void;
onReconnectAttempt?: (info: { attempt: number; delayMs: number }) => void;
onClose?: (info: { reason: 'unsubscribe' | 'complete' | 'retriesExceeded' }) => void;
}Global hooks via DI:
import { ApplicationConfig } from '@angular/core';
import { SSE_CLIENT_CONFIG, SseClientHooks } from '@spectrayan/ng-sse-client';
const hooks: SseClientHooks = {
onConnect: (url) => console.log('[SSE] connecting to', url),
onOpen: ({ attempt }) => console.log('[SSE] open after attempts:', attempt),
onMessage: ({ eventType }) => console.log('[SSE] event', eventType),
onError: ({ willRetry, nextDelayMs }) => console.warn('[SSE] error, willRetry=', willRetry, 'delay=', nextDelayMs),
onReconnectAttempt: ({ attempt, delayMs }) => console.log('[SSE] reconnect attempt', attempt, 'after', delayMs, 'ms'),
onClose: ({ reason }) => console.log('[SSE] closed because', reason),
};
export const appConfig: ApplicationConfig = {
providers: [
{ provide: SSE_CLIENT_CONFIG, useValue: { hooks }},
],
};Per-call overrides:
sse.stream<any>('http://localhost:8080/sse/user', {
hooks: {
onMessage: ({ data }) => console.log('stream got', data),
},
});SseReconnectionConfig controls exponential backoff and jitter used when the underlying EventSource errors:
export interface SseReconnectionConfig {
enabled: boolean;
maxRetries: number; // -1 = infinite
initialDelayMs: number; // default 1000
maxDelayMs: number; // default 30000
backoffMultiplier: number; // default 2
jitterRatio: number; // default 0.2 (randomize +-20%)
}The delay for retry N is computed with computeBackoffDelay() then bounded by maxDelayMs and randomized by jitterRatio.
If the server supports Last-Event-ID, the client tracks the last received event id (from MessageEvent.lastEventId) and appends ?lastEventId=<id> to the URL on reconnect. Use lastEventIdParamName to change the parameter name.
Unsubscribe to close the EventSource and remove all listeners. The library sets up teardown logic so there are no dangling connections.
If you need to provide a custom EventSource (for polyfills, testing, or environments), implement EventSourceLike and provide a custom EventSourceFactory via DI. See:
libs/ng-sse-client/src/lib/event-source.factory.ts-
libs/ng-sse-client/src/index.tsexports
- Parser errors surface as
erroron the returnedObservable. - Network errors trigger the reconnection flow if enabled; otherwise the
Observableerrors and completes. - Callback HTTP errors are logged and do not fail the SSE stream.
SSE requires a browser-like environment. On the server (Angular Universal), avoid creating EventSource. Guard code paths or inject a factory that no-ops on the server.
- If your SSE endpoint requires cookies or auth headers managed by the browser, set
withCredentials: truein options or global config. - Ensure the server sets appropriate CORS headers for EventSource (e.g.,
Access-Control-Allow-Origin,Access-Control-Allow-Credentials).
- Angular sample app (consumer):
samples/ng-sse-client-app— demonstrates multiple users, notifications, reconnection, and callbacks. Key file:samples/ng-sse-client-app/src/app/app.ts. - Spring WebFlux sample server:
samples/sse-sample-server-app— emitsmessageandnotificationevents.
Build the library
npx nx build ng-sse-clientRun tests
npx nx test ng-sse-client --ci --codeCoverage=falseApache-2.0
Details
- ng-sse-client
-
spectrayan
- 4 months ago
- Apache-2.0
- 4 dependencies
Assets
- ng-sse-client-1.0.0.tgz
Download activity
- Total downloads 2
- Last 30 days 0
- Last week 0
- Today 0