@horizon-republic/nestjs-jetstream 2.2.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -80
- package/dist/index.cjs +2068 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +995 -0
- package/dist/index.d.ts +995 -13
- package/dist/index.js +2068 -39
- package/dist/index.js.map +1 -1
- package/package.json +29 -19
- package/dist/client/index.d.ts +0 -3
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/index.js +0 -9
- package/dist/client/index.js.map +0 -1
- package/dist/client/jetstream.client.d.ts +0 -76
- package/dist/client/jetstream.client.d.ts.map +0 -1
- package/dist/client/jetstream.client.js +0 -325
- package/dist/client/jetstream.client.js.map +0 -1
- package/dist/client/jetstream.record.d.ts +0 -55
- package/dist/client/jetstream.record.d.ts.map +0 -1
- package/dist/client/jetstream.record.js +0 -84
- package/dist/client/jetstream.record.js.map +0 -1
- package/dist/codec/index.d.ts +0 -2
- package/dist/codec/index.d.ts.map +0 -1
- package/dist/codec/index.js +0 -6
- package/dist/codec/index.js.map +0 -1
- package/dist/codec/json.codec.d.ts +0 -20
- package/dist/codec/json.codec.d.ts.map +0 -1
- package/dist/codec/json.codec.js +0 -30
- package/dist/codec/json.codec.js.map +0 -1
- package/dist/connection/connection.provider.d.ts +0 -50
- package/dist/connection/connection.provider.d.ts.map +0 -1
- package/dist/connection/connection.provider.js +0 -141
- package/dist/connection/connection.provider.js.map +0 -1
- package/dist/connection/index.d.ts +0 -2
- package/dist/connection/index.d.ts.map +0 -1
- package/dist/connection/index.js +0 -6
- package/dist/connection/index.js.map +0 -1
- package/dist/context/index.d.ts +0 -2
- package/dist/context/index.d.ts.map +0 -1
- package/dist/context/index.js +0 -6
- package/dist/context/index.js.map +0 -1
- package/dist/context/rpc.context.d.ts +0 -35
- package/dist/context/rpc.context.d.ts.map +0 -1
- package/dist/context/rpc.context.js +0 -44
- package/dist/context/rpc.context.js.map +0 -1
- package/dist/health/index.d.ts +0 -3
- package/dist/health/index.d.ts.map +0 -1
- package/dist/health/index.js +0 -6
- package/dist/health/index.js.map +0 -1
- package/dist/health/jetstream.health-indicator.d.ts +0 -47
- package/dist/health/jetstream.health-indicator.d.ts.map +0 -1
- package/dist/health/jetstream.health-indicator.js +0 -85
- package/dist/health/jetstream.health-indicator.js.map +0 -1
- package/dist/hooks/event-bus.d.ts +0 -31
- package/dist/hooks/event-bus.d.ts.map +0 -1
- package/dist/hooks/event-bus.js +0 -79
- package/dist/hooks/event-bus.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -6
- package/dist/hooks/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/interfaces/client.interface.d.ts +0 -14
- package/dist/interfaces/client.interface.d.ts.map +0 -1
- package/dist/interfaces/client.interface.js +0 -3
- package/dist/interfaces/client.interface.js.map +0 -1
- package/dist/interfaces/codec.interface.d.ts +0 -28
- package/dist/interfaces/codec.interface.d.ts.map +0 -1
- package/dist/interfaces/codec.interface.js +0 -3
- package/dist/interfaces/codec.interface.js.map +0 -1
- package/dist/interfaces/hooks.interface.d.ts +0 -71
- package/dist/interfaces/hooks.interface.d.ts.map +0 -1
- package/dist/interfaces/hooks.interface.js +0 -16
- package/dist/interfaces/hooks.interface.js.map +0 -1
- package/dist/interfaces/index.d.ts +0 -8
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/index.js +0 -6
- package/dist/interfaces/index.js.map +0 -1
- package/dist/interfaces/options.interface.d.ts +0 -142
- package/dist/interfaces/options.interface.d.ts.map +0 -1
- package/dist/interfaces/options.interface.js +0 -3
- package/dist/interfaces/options.interface.js.map +0 -1
- package/dist/interfaces/routing.interface.d.ts +0 -15
- package/dist/interfaces/routing.interface.d.ts.map +0 -1
- package/dist/interfaces/routing.interface.js +0 -3
- package/dist/interfaces/routing.interface.js.map +0 -1
- package/dist/interfaces/stream.interface.d.ts +0 -5
- package/dist/interfaces/stream.interface.d.ts.map +0 -1
- package/dist/interfaces/stream.interface.js +0 -3
- package/dist/interfaces/stream.interface.js.map +0 -1
- package/dist/jetstream.constants.d.ts +0 -58
- package/dist/jetstream.constants.d.ts.map +0 -1
- package/dist/jetstream.constants.js +0 -168
- package/dist/jetstream.constants.js.map +0 -1
- package/dist/jetstream.module.d.ts +0 -89
- package/dist/jetstream.module.d.ts.map +0 -1
- package/dist/jetstream.module.js +0 -410
- package/dist/jetstream.module.js.map +0 -1
- package/dist/server/core-rpc.server.d.ts +0 -31
- package/dist/server/core-rpc.server.d.ts.map +0 -1
- package/dist/server/core-rpc.server.js +0 -95
- package/dist/server/core-rpc.server.js.map +0 -1
- package/dist/server/index.d.ts +0 -5
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js +0 -16
- package/dist/server/index.js.map +0 -1
- package/dist/server/infrastructure/consumer.provider.d.ts +0 -36
- package/dist/server/infrastructure/consumer.provider.d.ts.map +0 -1
- package/dist/server/infrastructure/consumer.provider.js +0 -123
- package/dist/server/infrastructure/consumer.provider.js.map +0 -1
- package/dist/server/infrastructure/index.d.ts +0 -4
- package/dist/server/infrastructure/index.d.ts.map +0 -1
- package/dist/server/infrastructure/index.js +0 -10
- package/dist/server/infrastructure/index.js.map +0 -1
- package/dist/server/infrastructure/message.provider.d.ts +0 -46
- package/dist/server/infrastructure/message.provider.d.ts.map +0 -1
- package/dist/server/infrastructure/message.provider.js +0 -100
- package/dist/server/infrastructure/message.provider.js.map +0 -1
- package/dist/server/infrastructure/stream.provider.d.ts +0 -38
- package/dist/server/infrastructure/stream.provider.d.ts.map +0 -1
- package/dist/server/infrastructure/stream.provider.js +0 -109
- package/dist/server/infrastructure/stream.provider.js.map +0 -1
- package/dist/server/routing/event.router.d.ts +0 -56
- package/dist/server/routing/event.router.d.ts.map +0 -1
- package/dist/server/routing/event.router.js +0 -132
- package/dist/server/routing/event.router.js.map +0 -1
- package/dist/server/routing/index.d.ts +0 -5
- package/dist/server/routing/index.d.ts.map +0 -1
- package/dist/server/routing/index.js +0 -10
- package/dist/server/routing/index.js.map +0 -1
- package/dist/server/routing/pattern-registry.d.ts +0 -39
- package/dist/server/routing/pattern-registry.d.ts.map +0 -1
- package/dist/server/routing/pattern-registry.js +0 -116
- package/dist/server/routing/pattern-registry.js.map +0 -1
- package/dist/server/routing/rpc.router.d.ts +0 -37
- package/dist/server/routing/rpc.router.d.ts.map +0 -1
- package/dist/server/routing/rpc.router.js +0 -121
- package/dist/server/routing/rpc.router.js.map +0 -1
- package/dist/server/strategy.d.ts +0 -55
- package/dist/server/strategy.d.ts.map +0 -1
- package/dist/server/strategy.js +0 -113
- package/dist/server/strategy.js.map +0 -1
- package/dist/shutdown/index.d.ts +0 -2
- package/dist/shutdown/index.d.ts.map +0 -1
- package/dist/shutdown/index.js +0 -6
- package/dist/shutdown/index.js.map +0 -1
- package/dist/shutdown/shutdown.manager.d.ts +0 -27
- package/dist/shutdown/shutdown.manager.d.ts.map +0 -1
- package/dist/shutdown/shutdown.manager.js +0 -45
- package/dist/shutdown/shutdown.manager.js.map +0 -1
- package/dist/utils/index.d.ts +0 -3
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -8
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/serialize-error.d.ts +0 -10
- package/dist/utils/serialize-error.d.ts.map +0 -1
- package/dist/utils/serialize-error.js +0 -21
- package/dist/utils/serialize-error.js.map +0 -1
- package/dist/utils/unwrap-result.d.ts +0 -15
- package/dist/utils/unwrap-result.d.ts.map +0 -1
- package/dist/utils/unwrap-result.js +0 -49
- package/dist/utils/unwrap-result.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ A production-grade NestJS transport for NATS JetStream with built-in support for
|
|
|
5
5
|
[](https://www.npmjs.com/package/@horizon-republic/nestjs-jetstream)
|
|
6
6
|
[](https://codecov.io/github/HorizonRepublic/nestjs-jetstream)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://badge.socket.dev/npm/package/@horizon-republic/nestjs-jetstream)
|
|
8
9
|
|
|
9
10
|
## Table of Contents
|
|
10
11
|
|
|
@@ -15,23 +16,25 @@ A production-grade NestJS transport for NATS JetStream with built-in support for
|
|
|
15
16
|
- [forRoot / forRootAsync](#forroot--forrootasync)
|
|
16
17
|
- [forFeature](#forfeature)
|
|
17
18
|
- [Full Options Reference](#full-options-reference)
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
- [
|
|
26
|
-
- [
|
|
27
|
-
- [
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [Edge Cases & Important Notes](#edge-cases--important-notes)
|
|
31
|
-
- [NATS Naming Conventions](#nats-naming-conventions)
|
|
32
|
-
- [Default Stream & Consumer Configs](#default-stream--consumer-configs)
|
|
33
|
-
- [API Reference](#api-reference)
|
|
34
|
-
- [
|
|
19
|
+
- [Messaging Patterns](#messaging-patterns)
|
|
20
|
+
- [RPC (Request/Reply)](#rpc-requestreply)
|
|
21
|
+
- [Events](#events)
|
|
22
|
+
- [JetstreamRecord Builder](#jetstreamrecord-builder)
|
|
23
|
+
- [Handler Context & Serialization](#handler-context--serialization)
|
|
24
|
+
- [RpcContext](#rpccontext)
|
|
25
|
+
- [Custom Codec](#custom-codec)
|
|
26
|
+
- [Operations](#operations)
|
|
27
|
+
- [Lifecycle Hooks](#lifecycle-hooks)
|
|
28
|
+
- [Health Checks](#health-checks)
|
|
29
|
+
- [Graceful Shutdown](#graceful-shutdown)
|
|
30
|
+
- [Reference](#reference)
|
|
31
|
+
- [Edge Cases & Important Notes](#edge-cases--important-notes)
|
|
32
|
+
- [NATS Naming Conventions](#nats-naming-conventions)
|
|
33
|
+
- [Default Stream & Consumer Configs](#default-stream--consumer-configs)
|
|
34
|
+
- [API Reference](#api-reference)
|
|
35
|
+
- [Development](#development)
|
|
36
|
+
- [Testing](#testing)
|
|
37
|
+
- [Contributing](#contributing)
|
|
35
38
|
- [License](#license)
|
|
36
39
|
- [Links](#links)
|
|
37
40
|
|
|
@@ -315,9 +318,11 @@ rpc: {
|
|
|
315
318
|
}
|
|
316
319
|
```
|
|
317
320
|
|
|
318
|
-
##
|
|
321
|
+
## Messaging Patterns
|
|
319
322
|
|
|
320
|
-
###
|
|
323
|
+
### RPC (Request/Reply)
|
|
324
|
+
|
|
325
|
+
#### Core Mode (Default)
|
|
321
326
|
|
|
322
327
|
Uses NATS native `request/reply` for the lowest possible latency.
|
|
323
328
|
|
|
@@ -346,7 +351,7 @@ JetstreamModule.forRoot({
|
|
|
346
351
|
| No handler running | Client times out |
|
|
347
352
|
| Decode error | Error response returned to caller |
|
|
348
353
|
|
|
349
|
-
|
|
354
|
+
#### JetStream Mode
|
|
350
355
|
|
|
351
356
|
Commands are persisted in a JetStream stream. Responses flow back via NATS Core inbox.
|
|
352
357
|
|
|
@@ -377,9 +382,9 @@ JetstreamModule.forRoot({
|
|
|
377
382
|
|
|
378
383
|
> **Why `term` instead of `nak` for RPC errors?** Redelivering a failed command could cause duplicate side effects. The caller is responsible for retrying.
|
|
379
384
|
|
|
380
|
-
|
|
385
|
+
### Events
|
|
381
386
|
|
|
382
|
-
|
|
387
|
+
#### Workqueue Events
|
|
383
388
|
|
|
384
389
|
Each event is delivered to **one** handler instance (load-balanced). Messages are acked **after** the handler completes successfully.
|
|
385
390
|
|
|
@@ -427,7 +432,7 @@ JetstreamModule.forRoot({
|
|
|
427
432
|
})
|
|
428
433
|
```
|
|
429
434
|
|
|
430
|
-
|
|
435
|
+
#### Broadcast Events
|
|
431
436
|
|
|
432
437
|
Broadcast events are delivered to **all** subscribing services. Each service gets its own durable consumer on a shared `broadcast-stream`.
|
|
433
438
|
|
|
@@ -467,7 +472,7 @@ JetstreamModule.forRoot({
|
|
|
467
472
|
|
|
468
473
|
> **Note:** The broadcast stream is shared across all services — stream-level settings (e.g., `max_age`, `max_bytes`) affect everyone. Consumer-level settings are per-service.
|
|
469
474
|
|
|
470
|
-
|
|
475
|
+
### JetstreamRecord Builder
|
|
471
476
|
|
|
472
477
|
Attach custom headers and per-request timeouts using the builder pattern:
|
|
473
478
|
|
|
@@ -491,7 +496,6 @@ this.client.emit('user.created', record);
|
|
|
491
496
|
|--------------------|-------------------------------|
|
|
492
497
|
| `x-correlation-id` | RPC request/response matching |
|
|
493
498
|
| `x-reply-to` | JetStream RPC response inbox |
|
|
494
|
-
| `x-message-id` | Deduplication |
|
|
495
499
|
| `x-error` | RPC error response flag |
|
|
496
500
|
|
|
497
501
|
Attempting to set a reserved header throws an error at build time.
|
|
@@ -506,7 +510,41 @@ Attempting to set a reserved header throws an error at build time.
|
|
|
506
510
|
| `x-trace-id` | Available for distributed tracing |
|
|
507
511
|
| `x-span-id` | Available for distributed tracing |
|
|
508
512
|
|
|
509
|
-
##
|
|
513
|
+
## Handler Context & Serialization
|
|
514
|
+
|
|
515
|
+
### RpcContext
|
|
516
|
+
|
|
517
|
+
Execution context available in all handlers via `@Ctx()`:
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
import { Ctx, Payload, MessagePattern } from '@nestjs/microservices';
|
|
521
|
+
import { RpcContext } from '@horizon-republic/nestjs-jetstream';
|
|
522
|
+
|
|
523
|
+
@MessagePattern('user.get')
|
|
524
|
+
getUser(@Payload() data: GetUserDto, @Ctx() ctx: RpcContext) {
|
|
525
|
+
const subject = ctx.getSubject(); // Full NATS subject
|
|
526
|
+
const traceId = ctx.getHeader('x-trace-id'); // Single header value
|
|
527
|
+
const headers = ctx.getHeaders(); // All headers (MsgHdrs)
|
|
528
|
+
const isJs = ctx.isJetStream(); // true for JetStream messages
|
|
529
|
+
const msg = ctx.getMessage(); // Raw JsMsg | Msg (escape hatch)
|
|
530
|
+
|
|
531
|
+
return this.userService.findOne(data.id);
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Available methods:**
|
|
536
|
+
|
|
537
|
+
| Method | Returns | Description |
|
|
538
|
+
|------------------|------------------------|-------------------------------------------|
|
|
539
|
+
| `getSubject()` | `string` | NATS subject the message was published to |
|
|
540
|
+
| `getHeader(key)` | `string \| undefined` | Single header value by key |
|
|
541
|
+
| `getHeaders()` | `MsgHdrs \| undefined` | All NATS message headers |
|
|
542
|
+
| `isJetStream()` | `boolean` | Whether the message supports ack/nak/term |
|
|
543
|
+
| `getMessage()` | `JsMsg \| Msg` | Raw NATS message (escape hatch) |
|
|
544
|
+
|
|
545
|
+
Available on both `@EventPattern` and `@MessagePattern` handlers.
|
|
546
|
+
|
|
547
|
+
### Custom Codec
|
|
510
548
|
|
|
511
549
|
The library uses JSON by default. Implement the `Codec` interface for any serialization format:
|
|
512
550
|
|
|
@@ -542,39 +580,9 @@ JetstreamModule.forFeature({
|
|
|
542
580
|
|
|
543
581
|
> All services communicating with each other **must use the same codec**. A codec mismatch results in decode errors (`term`, no redelivery).
|
|
544
582
|
|
|
545
|
-
##
|
|
583
|
+
## Operations
|
|
546
584
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
```typescript
|
|
550
|
-
import { Ctx, Payload, MessagePattern } from '@nestjs/microservices';
|
|
551
|
-
import { RpcContext } from '@horizon-republic/nestjs-jetstream';
|
|
552
|
-
|
|
553
|
-
@MessagePattern('user.get')
|
|
554
|
-
getUser(@Payload() data: GetUserDto, @Ctx() ctx: RpcContext) {
|
|
555
|
-
const subject = ctx.getSubject(); // Full NATS subject
|
|
556
|
-
const traceId = ctx.getHeader('x-trace-id'); // Single header value
|
|
557
|
-
const headers = ctx.getHeaders(); // All headers (MsgHdrs)
|
|
558
|
-
const isJs = ctx.isJetStream(); // true for JetStream messages
|
|
559
|
-
const msg = ctx.getMessage(); // Raw JsMsg | Msg (escape hatch)
|
|
560
|
-
|
|
561
|
-
return this.userService.findOne(data.id);
|
|
562
|
-
}
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
**Available methods:**
|
|
566
|
-
|
|
567
|
-
| Method | Returns | Description |
|
|
568
|
-
|------------------|------------------------|-------------------------------------------|
|
|
569
|
-
| `getSubject()` | `string` | NATS subject the message was published to |
|
|
570
|
-
| `getHeader(key)` | `string \| undefined` | Single header value by key |
|
|
571
|
-
| `getHeaders()` | `MsgHdrs \| undefined` | All NATS message headers |
|
|
572
|
-
| `isJetStream()` | `boolean` | Whether the message supports ack/nak/term |
|
|
573
|
-
| `getMessage()` | `JsMsg \| Msg` | Raw NATS message (escape hatch) |
|
|
574
|
-
|
|
575
|
-
Available on both `@EventPattern` and `@MessagePattern` handlers.
|
|
576
|
-
|
|
577
|
-
## Lifecycle Hooks
|
|
585
|
+
### Lifecycle Hooks
|
|
578
586
|
|
|
579
587
|
Subscribe to transport events for monitoring, alerting, or custom logic:
|
|
580
588
|
|
|
@@ -615,7 +623,7 @@ JetstreamModule.forRoot({
|
|
|
615
623
|
| `shutdownComplete` | `()` | `Logger.log` |
|
|
616
624
|
| `deadLetter` | `(info: DeadLetterInfo)` | `Logger.warn` |
|
|
617
625
|
|
|
618
|
-
|
|
626
|
+
#### Dead Letter Queue (DLQ)
|
|
619
627
|
|
|
620
628
|
When an event handler fails on every delivery attempt (`max_deliver`), the message becomes a "dead letter." By default, NATS terminates it silently. Configure `onDeadLetter` to intercept these messages:
|
|
621
629
|
|
|
@@ -661,7 +669,7 @@ JetstreamModule.forRootAsync({
|
|
|
661
669
|
});
|
|
662
670
|
```
|
|
663
671
|
|
|
664
|
-
|
|
672
|
+
### Health Checks
|
|
665
673
|
|
|
666
674
|
`JetstreamHealthIndicator` is automatically registered and exported by `forRoot()`. It checks NATS connection status and measures round-trip latency. `@nestjs/terminus` is **not required** — the indicator follows the Terminus API convention so it works seamlessly when Terminus is present, but can also be used standalone.
|
|
667
675
|
|
|
@@ -701,7 +709,7 @@ const status = await this.jetstream.check();
|
|
|
701
709
|
| `check()` | `JetstreamHealthStatus` | Never |
|
|
702
710
|
| `isHealthy(key?)` | `{ [key]: { status: 'up', ... } }` | On unhealthy (Terminus convention) |
|
|
703
711
|
|
|
704
|
-
|
|
712
|
+
### Graceful Shutdown
|
|
705
713
|
|
|
706
714
|
The transport shuts down automatically via NestJS `onApplicationShutdown()`:
|
|
707
715
|
|
|
@@ -719,13 +727,15 @@ JetstreamModule.forRoot({
|
|
|
719
727
|
|
|
720
728
|
No manual shutdown code needed.
|
|
721
729
|
|
|
722
|
-
##
|
|
730
|
+
## Reference
|
|
731
|
+
|
|
732
|
+
### Edge Cases & Important Notes
|
|
723
733
|
|
|
724
|
-
|
|
734
|
+
#### Event handlers must be idempotent
|
|
725
735
|
|
|
726
736
|
Events use at-least-once delivery. If your handler throws, the message is `nak`'d and NATS redelivers it (up to `max_deliver` times, default 3). Design handlers to be safe for repeated execution.
|
|
727
737
|
|
|
728
|
-
|
|
738
|
+
#### RPC error handling
|
|
729
739
|
|
|
730
740
|
The transport fully supports NestJS `RpcException` and custom exception filters. Throw `RpcException` with any payload — it will be delivered to the caller as-is:
|
|
731
741
|
|
|
@@ -750,11 +760,11 @@ this.client.send('user.update', data).subscribe({
|
|
|
750
760
|
|
|
751
761
|
In JetStream mode, failed RPC messages are `term`'d (not `nak`'d) to prevent duplicate side effects. The caller is responsible for implementing retry logic.
|
|
752
762
|
|
|
753
|
-
|
|
763
|
+
#### Fire-and-forget events
|
|
754
764
|
|
|
755
765
|
This library focuses on **reliable, persistent** event delivery via JetStream. If you need fire-and-forget (no persistence, no ack) for high-throughput scenarios, use the standard [NestJS NATS transport](https://docs.nestjs.com/microservices/nats) — it works perfectly alongside this library on the same NATS server.
|
|
756
766
|
|
|
757
|
-
|
|
767
|
+
#### Publisher-only mode
|
|
758
768
|
|
|
759
769
|
For services that only send messages (e.g., API gateways), disable consumer infrastructure:
|
|
760
770
|
|
|
@@ -766,17 +776,17 @@ JetstreamModule.forRoot({
|
|
|
766
776
|
})
|
|
767
777
|
```
|
|
768
778
|
|
|
769
|
-
|
|
779
|
+
#### Broadcast stream is shared
|
|
770
780
|
|
|
771
781
|
All services share a single `broadcast-stream`. Each service creates its own durable consumer with `filter_subjects` matching only its registered broadcast patterns. Stream-level configuration (`broadcast.stream`) affects all services.
|
|
772
782
|
|
|
773
783
|
Broadcast consumers use the same ack/nak semantics as workqueue consumers. Because each service has an **isolated durable consumer**, a `nak` (retry) from one service only causes redelivery to that specific service — other consumers are unaffected. This gives broadcast **at-least-once delivery per consumer** with independent retry.
|
|
774
784
|
|
|
775
|
-
|
|
785
|
+
#### Connection failure behavior
|
|
776
786
|
|
|
777
|
-
If the initial NATS connection is refused, the module throws
|
|
787
|
+
If the initial NATS connection is refused, the module throws an `Error` immediately (fail fast). For transient disconnects after startup, NATS handles reconnection automatically and the `reconnect` hook fires.
|
|
778
788
|
|
|
779
|
-
|
|
789
|
+
#### Observable return values
|
|
780
790
|
|
|
781
791
|
Handlers can return Observables. The transport takes the **first emitted value** for RPC responses and awaits completion for events:
|
|
782
792
|
|
|
@@ -792,15 +802,15 @@ handleOrder(@Payload() data: OrderDto): Observable<void> {
|
|
|
792
802
|
}
|
|
793
803
|
```
|
|
794
804
|
|
|
795
|
-
|
|
805
|
+
#### Consumer self-healing
|
|
796
806
|
|
|
797
|
-
If a JetStream consumer's message iterator ends unexpectedly (e.g., NATS restart), the transport automatically re-establishes consumption
|
|
807
|
+
If a JetStream consumer's message iterator ends unexpectedly (e.g., NATS restart), the transport automatically re-establishes consumption with exponential backoff (100ms up to 30s). This is logged as a warning.
|
|
798
808
|
|
|
799
|
-
|
|
809
|
+
#### NATS header size
|
|
800
810
|
|
|
801
811
|
Custom headers are transmitted as NATS message headers. NATS has a default header size limit. If you're attaching large metadata, consider putting it in the message body instead.
|
|
802
812
|
|
|
803
|
-
|
|
813
|
+
### NATS Naming Conventions
|
|
804
814
|
|
|
805
815
|
The transport generates NATS subjects, streams, and consumers based on the service `name`:
|
|
806
816
|
|
|
@@ -817,7 +827,7 @@ The transport generates NATS subjects, streams, and consumers based on the servi
|
|
|
817
827
|
| Command consumer | `{internal}_cmd-consumer` | `orders__microservice_cmd-consumer` |
|
|
818
828
|
| Broadcast consumer | `{internal}_broadcast-consumer` | `orders__microservice_broadcast-consumer` |
|
|
819
829
|
|
|
820
|
-
|
|
830
|
+
### Default Stream & Consumer Configs
|
|
821
831
|
|
|
822
832
|
All defaults can be overridden via `events`, `broadcast`, or `rpc` options.
|
|
823
833
|
|
|
@@ -904,9 +914,9 @@ All defaults can be overridden via `events`, `broadcast`, or `rpc` options.
|
|
|
904
914
|
|
|
905
915
|
</details>
|
|
906
916
|
|
|
907
|
-
|
|
917
|
+
### API Reference
|
|
908
918
|
|
|
909
|
-
|
|
919
|
+
#### Exports
|
|
910
920
|
|
|
911
921
|
```typescript
|
|
912
922
|
// Module
|
|
@@ -950,7 +960,7 @@ StreamConsumerOverrides
|
|
|
950
960
|
TransportHooks
|
|
951
961
|
```
|
|
952
962
|
|
|
953
|
-
|
|
963
|
+
#### Helper: `nanos(ms)`
|
|
954
964
|
|
|
955
965
|
Convert milliseconds to nanoseconds (required by NATS JetStream config):
|
|
956
966
|
|
|
@@ -964,7 +974,46 @@ events: {
|
|
|
964
974
|
}
|
|
965
975
|
```
|
|
966
976
|
|
|
967
|
-
##
|
|
977
|
+
## Development
|
|
978
|
+
|
|
979
|
+
### Testing
|
|
980
|
+
|
|
981
|
+
The project uses [Vitest](https://vitest.dev/) with two test suites configured as [projects](https://vitest.dev/guide/workspace):
|
|
982
|
+
|
|
983
|
+
```bash
|
|
984
|
+
# Unit tests (no external dependencies)
|
|
985
|
+
pnpm test
|
|
986
|
+
|
|
987
|
+
# Integration tests (requires a running NATS server with JetStream)
|
|
988
|
+
pnpm test:integration
|
|
989
|
+
|
|
990
|
+
# Both suites sequentially
|
|
991
|
+
pnpm test:all
|
|
992
|
+
|
|
993
|
+
# Unit tests in watch mode
|
|
994
|
+
pnpm test:watch
|
|
995
|
+
|
|
996
|
+
# Unit tests with coverage report
|
|
997
|
+
pnpm test:cov
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
#### Running NATS locally
|
|
1001
|
+
|
|
1002
|
+
Integration tests require a NATS server with JetStream enabled:
|
|
1003
|
+
|
|
1004
|
+
```bash
|
|
1005
|
+
docker run -d --name nats -p 4222:4222 nats:latest -js
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
#### Writing tests
|
|
1009
|
+
|
|
1010
|
+
- Use `sut` (system under test) for the main instance
|
|
1011
|
+
- Use `createMock<T>()` from `@golevelup/ts-vitest` for mocking
|
|
1012
|
+
- Follow Given-When-Then structure with comments
|
|
1013
|
+
- Order: happy path → edge cases → error cases
|
|
1014
|
+
- Always include `afterEach(vi.resetAllMocks)`
|
|
1015
|
+
|
|
1016
|
+
### Contributing
|
|
968
1017
|
|
|
969
1018
|
Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
970
1019
|
|
|
@@ -979,4 +1028,4 @@ Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for
|
|
|
979
1028
|
- [GitHub Repository](https://github.com/HorizonRepublic/nestjs-jetstream)
|
|
980
1029
|
- [npm Package](https://www.npmjs.com/package/@horizon-republic/nestjs-jetstream)
|
|
981
1030
|
- [Report bugs](https://github.com/HorizonRepublic/nestjs-jetstream/issues)
|
|
982
|
-
- [Discussions](https://github.com/HorizonRepublic/nestjs-jetstream/discussions)
|
|
1031
|
+
- [Discussions](https://github.com/HorizonRepublic/nestjs-jetstream/discussions)
|