Skip to content

Commit c054b92

Browse files
committed
docs: clarify SSE supports for different clients
1 parent d7e660e commit c054b92

37 files changed

+3720
-25
lines changed

docs/openapi-ts/clients/angular.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,157 @@ This section is under construction. We appreciate your patience.
204204
This section is under construction. We appreciate your patience.
205205
:::
206206

207+
## Server-Sent Events
208+
209+
When your OpenAPI spec defines endpoints with `text/event-stream` responses, the SDK generates SSE-enabled functions that return an async stream instead of a regular response.
210+
211+
::: warning
212+
Angular `HttpClient` interceptors do not apply to SSE connections. The SSE client uses the native Fetch API under the hood.
213+
:::
214+
215+
### Consuming a stream
216+
217+
```js
218+
import { watchStockPrices } from './client/sdk.gen';
219+
220+
const { stream } = await watchStockPrices();
221+
222+
for await (const event of stream) {
223+
console.log(event);
224+
}
225+
```
226+
227+
For more details on how to use the SSE-enabled functions, refer to the [SDK documentation](/openapi-ts/plugins/sdk#server-sent-events).
228+
229+
### Angular component example
230+
231+
When using the [`@Injectable` configuration](#injectable), SSE methods are instance methods on the generated service classes.
232+
233+
```ts
234+
// openapi-ts.config.ts
235+
export default defineConfig({
236+
plugins: [
237+
'@hey-api/client-angular',
238+
{
239+
name: '@hey-api/sdk',
240+
asClass: true,
241+
operations: {
242+
containerName: '{{name}}Service',
243+
strategy: 'byTags',
244+
},
245+
},
246+
],
247+
});
248+
```
249+
250+
The generated service has SSE methods alongside regular methods:
251+
252+
```ts
253+
// Generated: sdk.gen.ts
254+
@Injectable({ providedIn: 'root' })
255+
export class StockService {
256+
public watchStockPrices<ThrowOnError extends boolean = false>(options?) {
257+
return (options?.client ?? client).sse.get<...>({ url: '/stock/watch', ...options });
258+
}
259+
}
260+
```
261+
262+
Inject the service in your component. Use signals for state and `ngOnDestroy` for cleanup.
263+
264+
```ts
265+
import { Component, inject, OnDestroy, signal } from '@angular/core';
266+
import { StockService } from './client/sdk.gen';
267+
import type { StockUpdate } from './client/types.gen';
268+
269+
@Component({
270+
selector: 'app-stock-ticker',
271+
template: `
272+
<button (click)="connect()">Connect</button>
273+
<button (click)="disconnect()">Disconnect</button>
274+
<p>Status: {{ status() }}</p>
275+
<ul>
276+
@for (update of updates(); track $index) {
277+
<li>{{ update | json }}</li>
278+
}
279+
</ul>
280+
`,
281+
})
282+
export class StockTickerComponent implements OnDestroy {
283+
#stockService = inject(StockService);
284+
285+
updates = signal<StockUpdate[]>([]);
286+
status = signal<'connected' | 'disconnected' | 'error'>('disconnected');
287+
#controller: AbortController | null = null;
288+
289+
async connect() {
290+
this.#controller = new AbortController();
291+
this.status.set('connected');
292+
this.updates.set([]);
293+
294+
try {
295+
const { stream } = await this.#stockService.watchStockPrices({
296+
signal: this.#controller.signal,
297+
});
298+
299+
for await (const event of stream) {
300+
this.updates.update((prev) => [...prev, event]);
301+
}
302+
} catch {
303+
if (!this.#controller?.signal.aborted) {
304+
this.status.set('error');
305+
return;
306+
}
307+
}
308+
this.status.set('disconnected');
309+
}
310+
311+
disconnect() {
312+
this.#controller?.abort();
313+
this.#controller = null;
314+
this.status.set('disconnected');
315+
}
316+
317+
ngOnDestroy() {
318+
this.disconnect();
319+
}
320+
}
321+
```
322+
323+
### RxJS alternative
324+
325+
If your codebase uses RxJS pipelines, you can wrap the async generator in an Observable.
326+
327+
```ts
328+
import { Observable } from 'rxjs';
329+
import { watchStockPrices } from './client/sdk.gen';
330+
import type { StockUpdate } from './client/types.gen';
331+
332+
function watchPrices$(): Observable<StockUpdate> {
333+
return new Observable((subscriber) => {
334+
const controller = new AbortController();
335+
336+
(async () => {
337+
try {
338+
const { stream } = await watchStockPrices({
339+
signal: controller.signal,
340+
});
341+
342+
for await (const event of stream) {
343+
subscriber.next(event);
344+
}
345+
subscriber.complete();
346+
} catch (error) {
347+
if (!controller.signal.aborted) {
348+
subscriber.error(error);
349+
}
350+
}
351+
})();
352+
353+
return () => controller.abort();
354+
});
355+
}
356+
```
357+
207358
## Build URL
208359

209360
If you need to access the compiled URL, you can use the `buildUrl()` method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

docs/openapi-ts/clients/angular/v19.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,157 @@ This section is under construction. We appreciate your patience.
204204
This section is under construction. We appreciate your patience.
205205
:::
206206

207+
## Server-Sent Events
208+
209+
When your OpenAPI spec defines endpoints with `text/event-stream` responses, the SDK generates SSE-enabled functions that return an async stream instead of a regular response.
210+
211+
::: warning
212+
Angular `HttpClient` interceptors do not apply to SSE connections. The SSE client uses the native Fetch API under the hood.
213+
:::
214+
215+
### Consuming a stream
216+
217+
```js
218+
import { watchStockPrices } from './client/sdk.gen';
219+
220+
const { stream } = await watchStockPrices();
221+
222+
for await (const event of stream) {
223+
console.log(event);
224+
}
225+
```
226+
227+
For more details on how to use the SSE-enabled functions, refer to the [SDK documentation](/openapi-ts/plugins/sdk#server-sent-events).
228+
229+
### Angular component example
230+
231+
When using the [`@Injectable` configuration](#injectable), SSE methods are instance methods on the generated service classes.
232+
233+
```ts
234+
// openapi-ts.config.ts
235+
export default defineConfig({
236+
plugins: [
237+
'@hey-api/client-angular',
238+
{
239+
name: '@hey-api/sdk',
240+
asClass: true,
241+
operations: {
242+
containerName: '{{name}}Service',
243+
strategy: 'byTags',
244+
},
245+
},
246+
],
247+
});
248+
```
249+
250+
The generated service has SSE methods alongside regular methods:
251+
252+
```ts
253+
// Generated: sdk.gen.ts
254+
@Injectable({ providedIn: 'root' })
255+
export class StockService {
256+
public watchStockPrices<ThrowOnError extends boolean = false>(options?) {
257+
return (options?.client ?? client).sse.get<...>({ url: '/stock/watch', ...options });
258+
}
259+
}
260+
```
261+
262+
Inject the service in your component. Use signals for state and `ngOnDestroy` for cleanup.
263+
264+
```ts
265+
import { Component, inject, OnDestroy, signal } from '@angular/core';
266+
import { StockService } from './client/sdk.gen';
267+
import type { StockUpdate } from './client/types.gen';
268+
269+
@Component({
270+
selector: 'app-stock-ticker',
271+
template: `
272+
<button (click)="connect()">Connect</button>
273+
<button (click)="disconnect()">Disconnect</button>
274+
<p>Status: {{ status() }}</p>
275+
<ul>
276+
@for (update of updates(); track $index) {
277+
<li>{{ update | json }}</li>
278+
}
279+
</ul>
280+
`,
281+
})
282+
export class StockTickerComponent implements OnDestroy {
283+
#stockService = inject(StockService);
284+
285+
updates = signal<StockUpdate[]>([]);
286+
status = signal<'connected' | 'disconnected' | 'error'>('disconnected');
287+
#controller: AbortController | null = null;
288+
289+
async connect() {
290+
this.#controller = new AbortController();
291+
this.status.set('connected');
292+
this.updates.set([]);
293+
294+
try {
295+
const { stream } = await this.#stockService.watchStockPrices({
296+
signal: this.#controller.signal,
297+
});
298+
299+
for await (const event of stream) {
300+
this.updates.update((prev) => [...prev, event]);
301+
}
302+
} catch {
303+
if (!this.#controller?.signal.aborted) {
304+
this.status.set('error');
305+
return;
306+
}
307+
}
308+
this.status.set('disconnected');
309+
}
310+
311+
disconnect() {
312+
this.#controller?.abort();
313+
this.#controller = null;
314+
this.status.set('disconnected');
315+
}
316+
317+
ngOnDestroy() {
318+
this.disconnect();
319+
}
320+
}
321+
```
322+
323+
### RxJS alternative
324+
325+
If your codebase uses RxJS pipelines, you can wrap the async generator in an Observable.
326+
327+
```ts
328+
import { Observable } from 'rxjs';
329+
import { watchStockPrices } from './client/sdk.gen';
330+
import type { StockUpdate } from './client/types.gen';
331+
332+
function watchPrices$(): Observable<StockUpdate> {
333+
return new Observable((subscriber) => {
334+
const controller = new AbortController();
335+
336+
(async () => {
337+
try {
338+
const { stream } = await watchStockPrices({
339+
signal: controller.signal,
340+
});
341+
342+
for await (const event of stream) {
343+
subscriber.next(event);
344+
}
345+
subscriber.complete();
346+
} catch (error) {
347+
if (!controller.signal.aborted) {
348+
subscriber.error(error);
349+
}
350+
}
351+
})();
352+
353+
return () => controller.abort();
354+
});
355+
}
356+
```
357+
207358
## Build URL
208359

209360
If you need to access the compiled URL, you can use the `buildUrl()` method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

docs/openapi-ts/clients/axios.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,28 @@ client.instance.interceptors.request.use((config) => {
184184
});
185185
```
186186

187+
## Server-Sent Events
188+
189+
When your OpenAPI spec defines endpoints with `text/event-stream` responses, the SDK generates SSE-enabled functions that return an async stream instead of a regular response.
190+
191+
::: warning
192+
Axios interceptors registered through `client.instance.interceptors` do not apply to SSE connections. The SSE client uses the native Fetch API under the hood.
193+
:::
194+
195+
### Consuming a stream
196+
197+
```js
198+
import { watchStockPrices } from './client/sdk.gen';
199+
200+
const { stream } = await watchStockPrices();
201+
202+
for await (const event of stream) {
203+
console.log(event);
204+
}
205+
```
206+
207+
For more details on how to use the SSE-enabled functions, refer to the [SDK documentation](/openapi-ts/plugins/sdk#server-sent-events).
208+
187209
## Build URL
188210

189211
If you need to access the compiled URL, you can use the `buildUrl()` method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

docs/openapi-ts/clients/fetch.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,28 @@ client.interceptors.request.use((request, options) => {
262262
});
263263
```
264264

265+
## Server-Sent Events
266+
267+
When your OpenAPI spec defines endpoints with `text/event-stream` responses, the SDK generates SSE-enabled functions that return an async stream instead of a regular response.
268+
269+
::: info
270+
Request interceptors registered through `client.interceptors.request` apply to SSE connections, including on each reconnect attempt.
271+
:::
272+
273+
### Consuming a stream
274+
275+
```js
276+
import { watchStockPrices } from './client/sdk.gen';
277+
278+
const { stream } = await watchStockPrices();
279+
280+
for await (const event of stream) {
281+
console.log(event);
282+
}
283+
```
284+
285+
For more details on how to use the SSE-enabled functions, refer to the [SDK documentation](/openapi-ts/plugins/sdk#server-sent-events).
286+
265287
## Build URL
266288

267289
If you need to access the compiled URL, you can use the `buildUrl()` method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

0 commit comments

Comments
 (0)