@nats-io/transport-node 3.0.0-6

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 ADDED
@@ -0,0 +1,853 @@
1
+ # NATS.js - A [NATS](http://nats.io) client for [Node.Js](https://nodejs.org/en/)
2
+
3
+ A Node.js client for the [NATS messaging system](https://nats.io).
4
+
5
+ [![License](https://img.shields.io/badge/Licence-Apache%202.0-blue.svg)](LICENSE)
6
+ ![NATS.js CI](https://github.com/nats-io/nats.js/workflows/NATS.js%20CI/badge.svg)
7
+ [![npm](https://img.shields.io/npm/v/nats.svg)](https://www.npmjs.com/package/nats)
8
+ [![npm](https://img.shields.io/npm/dt/nats.svg)](https://www.npmjs.com/package/nats)
9
+ [![npm](https://img.shields.io/npm/dm/nats.svg)](https://www.npmjs.com/package/nats)
10
+
11
+ # Installation
12
+
13
+ ```bash
14
+ npm install nats@latest
15
+ ```
16
+
17
+ The nats.js@2.0.0 **is not API compatible** with previous versions of nats.js.
18
+ For a migration guide, please see [the migration guide](migration.md).
19
+
20
+ ### Documentation and the NATS Base Client
21
+
22
+ This repository implements the Node.js transport of the client. This transport
23
+ depends on a common module called the _NATS Base Client_ which lives along the
24
+ [nats.deno](https://github.com/nats-io/nats.deno/tree/main/nats-base-client).
25
+ The NATS Base Client provides the same API and functionality across all
26
+ JavaScript NATS clients supported by nats.io (nats.deno, nats.js and nats.ws).
27
+
28
+ While the best documentation is looking at code examples, you may want to browse
29
+ the [JSDoc documentation](https://nats-io.github.io/nats.deno). The best entry
30
+ point into the JS Doc is the
31
+ [NatsConnection](https://nats-io.github.io/nats.deno/interfaces/NatsConnection.html)
32
+ all functionality starts with a connection.
33
+
34
+ ## Basics
35
+
36
+ ### Connecting to a nats-server
37
+
38
+ To connect to a server you use the `connect()` function. It returns a connection
39
+ that you can use to interact with the server. You can customize the behavior of
40
+ the client by specifying many [`ConnectionOptions`](#connection-options).
41
+
42
+ By default, a connection will attempt a connection on`127.0.0.1:4222`. If the
43
+ connection is dropped, the client will attempt to reconnect. You can customize
44
+ the server you want to connect to by specifying `port` (for local connections),
45
+ or full host port on the `servers` option. Note that the `servers` option can be
46
+ a single hostport (a string) or an array of hostports.
47
+
48
+ The example below will attempt to connect to different servers by specifying
49
+ different `ConnectionOptions`. At least two of them should work if your internet
50
+ is working.
51
+
52
+ ```javascript
53
+ import { connect } from "nats";
54
+ const servers = [
55
+ {},
56
+ { servers: ["demo.nats.io:4442", "demo.nats.io:4222"] },
57
+ { servers: "demo.nats.io:4443" },
58
+ { port: 4222 },
59
+ { servers: "localhost" },
60
+ ];
61
+ await servers.forEach(async (v) => {
62
+ try {
63
+ const nc = await connect(v);
64
+ console.log(`connected to ${nc.getServer()}`);
65
+ // this promise indicates the client closed
66
+ const done = nc.closed();
67
+ // do something with the connection
68
+
69
+ // close the connection
70
+ await nc.close();
71
+ // check if the close was OK
72
+ const err = await done;
73
+ if (err) {
74
+ console.log(`error closing:`, err);
75
+ }
76
+ } catch (err) {
77
+ console.log(`error connecting to ${JSON.stringify(v)}`);
78
+ }
79
+ });
80
+ ```
81
+
82
+ To disconnect from the nats-server, call `close()` on the connection. A
83
+ connection can also be terminated when an unexpected error happens. For example,
84
+ the server returns a run-time error. In those cases, the client will re-initiate
85
+ a connection.
86
+
87
+ By default, the client will always attempt to reconnect if the connection is
88
+ closed for a reason other than calling `close()`. To get notified when the
89
+ connection is closed for some reason, await the resolution of the Promise
90
+ returned by `closed()`. If closed resolves to a value, the value is a
91
+ `NatsError` indicating why the connection closed.
92
+
93
+ ### Publish and Subscribe
94
+
95
+ The basic client operations are `publish` to send messages and `subscribe` to
96
+ receive messages.
97
+
98
+ Messages are published to a subject. A subject is like a URL with the exception
99
+ that it doesn't specify an actual endpoint. All recipients that have expressed
100
+ interest in a subject will receive messages addressed to that subject (provided
101
+ they have access and permissions to get it). To express interest in a subject,
102
+ you create a `subscription`.
103
+
104
+ In JavaScript clients (websocket, Deno, or Node) subscriptions work as an async
105
+ iterator - clients simply loop to process messages as they become available.
106
+
107
+ NATS messages are payload agnostic. Payloads are `Uint8Arrays`. You can easily
108
+ convert to and from JSON or strings by using `JSONCodec` or `StringCodec`, or a
109
+ custom `Codec`.
110
+
111
+ To cancel a subscription and terminate your interest, you call `unsubscribe()`
112
+ or `drain()` on a subscription. Unsubscribe will typically terminate regardless
113
+ of whether there are messages in flight for the client. Drain ensures that all
114
+ messages that are inflight are processed before canceling the subscription.
115
+ Connections can also be drained as well. Draining a connection closes it, after
116
+ all subscriptions have been drained and all outbound messages have been sent to
117
+ the server.
118
+
119
+ ```javascript
120
+ import { connect, StringCodec } from "nats";
121
+
122
+ // to create a connection to a nats-server:
123
+ const nc = await connect({ servers: "demo.nats.io:4222" });
124
+
125
+ // create a codec
126
+ const sc = StringCodec();
127
+ // create a simple subscriber and iterate over messages
128
+ // matching the subscription
129
+ const sub = nc.subscribe("hello");
130
+ (async () => {
131
+ for await (const m of sub) {
132
+ console.log(`[${sub.getProcessed()}]: ${sc.decode(m.data)}`);
133
+ }
134
+ console.log("subscription closed");
135
+ })();
136
+
137
+ nc.publish("hello", sc.encode("world"));
138
+ nc.publish("hello", sc.encode("again"));
139
+
140
+ // we want to ensure that messages that are in flight
141
+ // get processed, so we are going to drain the
142
+ // connection. Drain is the same as close, but makes
143
+ // sure that all messages in flight get seen
144
+ // by the iterator. After calling drain on the connection
145
+ // the connection closes.
146
+ await nc.drain();
147
+ ```
148
+
149
+ ### Wildcard Subscriptions
150
+
151
+ Subjects can be used to organize messages into hierarchies. For example, a
152
+ subject may contain additional information that can be useful in providing a
153
+ context to the message, such as the ID of the client that sent the message, or
154
+ the region where a message originated.
155
+
156
+ Instead of subscribing to each specific subject, you can create subscriptions
157
+ that have subjects with wildcards. Wildcards match one or more tokens in a
158
+ subject. A token is a string following a period.
159
+
160
+ All subscriptions are independent. If two different subscriptions match a
161
+ subject, both will get to process the message:
162
+
163
+ ```javascript
164
+ import { connect, StringCodec } from "nats";
165
+
166
+ const nc = await connect({ servers: "demo.nats.io:4222" });
167
+ const sc = StringCodec();
168
+
169
+ // subscriptions can have wildcard subjects
170
+ // the '*' matches any string in the specified token position
171
+ const s1 = nc.subscribe("help.*.system");
172
+ const s2 = nc.subscribe("help.me.*");
173
+ // the '>' matches any tokens in that position or following
174
+ // '>' can only be specified at the end of the subject
175
+ const s3 = nc.subscribe("help.>");
176
+
177
+ async function printMsgs(s) {
178
+ let subj = s.getSubject();
179
+ console.log(`listening for ${subj}`);
180
+ const c = 13 - subj.length;
181
+ const pad = "".padEnd(c);
182
+ for await (const m of s) {
183
+ console.log(
184
+ `[${subj}]${pad} #${s.getProcessed()} - ${m.subject} ${
185
+ m.data ? " " + sc.decode(m.data) : ""
186
+ }`,
187
+ );
188
+ }
189
+ }
190
+
191
+ printMsgs(s1);
192
+ printMsgs(s2);
193
+ printMsgs(s3);
194
+
195
+ // don't exit until the client closes
196
+ await nc.closed();
197
+ ```
198
+
199
+ ### Services: Request/Reply
200
+
201
+ Request/Reply is NATS equivalent to an HTTP request. To make requests you
202
+ publish messages as you did before, but also specify a `reply` subject. The
203
+ `reply` subject is where a service will publish your response.
204
+
205
+ NATS provides syntactic sugar, for publishing requests. The `request()` API will
206
+ generate a reply subject and manage the creation of a subscription under the
207
+ covers. It will also start a timer to ensure that if a response is not received
208
+ within your allotted time, the request fails. The example also illustrates a
209
+ graceful shutdown.
210
+
211
+ #### Services
212
+
213
+ Here's an example of a service. It is a bit more complicated than expected
214
+ simply to illustrate not only how to create responses, but how the subject
215
+ itself is used to dispatch different behaviors.
216
+
217
+ ```javascript
218
+ import { connect, StringCodec } from "nats";
219
+
220
+ // create a connection
221
+ const nc = await connect({ servers: "demo.nats.io" });
222
+
223
+ // create a codec
224
+ const sc = StringCodec();
225
+
226
+ // this subscription listens for `time` requests and returns the current time
227
+ const subscription = nc.subscribe("time");
228
+ (async (sub) => {
229
+ console.log(`listening for ${sub.getSubject()} requests...`);
230
+ for await (const m of sub) {
231
+ if (m.respond(sc.encode(new Date().toISOString()))) {
232
+ console.info(`[time] handled #${sub.getProcessed()}`);
233
+ } else {
234
+ console.log(`[time] #${sub.getProcessed()} ignored - no reply subject`);
235
+ }
236
+ }
237
+ console.log(`subscription ${sub.getSubject()} drained.`);
238
+ })(subscription);
239
+
240
+ // this subscription listens for admin.uptime and admin.stop
241
+ // requests to admin.uptime returns how long the service has been running
242
+ // requests to admin.stop gracefully stop the client by draining
243
+ // the connection
244
+ const started = Date.now();
245
+ const msub = nc.subscribe("admin.*");
246
+ (async (sub) => {
247
+ console.log(`listening for ${sub.getSubject()} requests [uptime | stop]`);
248
+ // it would be very good to verify the origin of the request
249
+ // before implementing something that allows your service to be managed.
250
+ // NATS can limit which client can send or receive on what subjects.
251
+ for await (const m of sub) {
252
+ const chunks = m.subject.split(".");
253
+ console.info(`[admin] #${sub.getProcessed()} handling ${chunks[1]}`);
254
+ switch (chunks[1]) {
255
+ case "uptime":
256
+ // send the number of millis since up
257
+ m.respond(sc.encode(`${Date.now() - started}`));
258
+ break;
259
+ case "stop": {
260
+ m.respond(sc.encode(`[admin] #${sub.getProcessed()} stopping....`));
261
+ // gracefully shutdown
262
+ nc.drain()
263
+ .catch((err) => {
264
+ console.log("error draining", err);
265
+ });
266
+ break;
267
+ }
268
+ default:
269
+ console.log(
270
+ `[admin] #${sub.getProcessed()} ignoring request for ${m.subject}`,
271
+ );
272
+ }
273
+ }
274
+ console.log(`subscription ${sub.getSubject()} drained.`);
275
+ })(msub);
276
+
277
+ // wait for the client to close here.
278
+ await nc.closed().then((err) => {
279
+ let m = `connection to ${nc.getServer()} closed`;
280
+ if (err) {
281
+ m = `${m} with an error: ${err.message}`;
282
+ }
283
+ console.log(m);
284
+ });
285
+ ```
286
+
287
+ #### Making Requests
288
+
289
+ Here's a simple example of a client making a simple request from the service
290
+ above:
291
+
292
+ ```javascript
293
+ import { connect, Empty, StringCodec } from "nats";
294
+
295
+ // create a connection
296
+ const nc = await connect({ servers: "demo.nats.io:4222" });
297
+
298
+ // create an encoder
299
+ const sc = StringCodec();
300
+
301
+ // the client makes a request and receives a promise for a message
302
+ // by default the request times out after 1s (1000 millis) and has
303
+ // no payload.
304
+ await nc.request("time", Empty, { timeout: 1000 })
305
+ .then((m) => {
306
+ console.log(`got response: ${sc.decode(m.data)}`);
307
+ })
308
+ .catch((err) => {
309
+ console.log(`problem with request: ${err.message}`);
310
+ });
311
+
312
+ await nc.close();
313
+ ```
314
+
315
+ ### Queue Groups
316
+
317
+ Queue groups allow scaling of services horizontally. Subscriptions for members
318
+ of a queue group are treated as a single service. When you send a message to a
319
+ queue group subscription, only a single client in a queue group will receive it.
320
+
321
+ There can be any number of queue groups. Each group is treated as its own
322
+ independent unit. Note that non-queue subscriptions are also independent of
323
+ subscriptions in a queue group.
324
+
325
+ ```javascript
326
+ import {
327
+ connect,
328
+ NatsConnection,
329
+ StringCodec,
330
+ } from "nats";
331
+
332
+ async function createService(
333
+ name,
334
+ count = 1,
335
+ queue = ""
336
+ ): Promise {
337
+ const conns = [];
338
+ for (let i = 1; i <= count; i++) {
339
+ const n = queue ? `${name}-${i}` : name;
340
+ const nc = await connect(
341
+ { servers: "demo.nats.io:4222", name: `${n}` },
342
+ );
343
+ nc.closed()
344
+ .then((err) => {
345
+ if (err) {
346
+ console.error(
347
+ `service ${n} exited because of error: ${err.message}`,
348
+ );
349
+ }
350
+ });
351
+ // create a subscription - note the option for a queue, if set
352
+ // any client with the same queue will be a member of the group.
353
+ const sub = nc.subscribe("echo", { queue: queue });
354
+ const _ = handleRequest(n, sub);
355
+ console.log(`${nc.options.name} is listening for 'echo' requests...`);
356
+ conns.push(nc);
357
+ }
358
+ return conns;
359
+ }
360
+
361
+ const sc = StringCodec();
362
+
363
+ // simple handler for service requests
364
+ async function handleRequest(name, s) {
365
+ const p = 12 - name.length;
366
+ const pad = "".padEnd(p);
367
+ for await (const m of s) {
368
+ // respond returns true if the message had a reply subject, thus it could respond
369
+ if (m.respond(m.data)) {
370
+ console.log(
371
+ `[${name}]:${pad} #${s.getProcessed()} echoed ${sc.decode(m.data)}`,
372
+ );
373
+ } else {
374
+ console.log(
375
+ `[${name}]:${pad} #${s.getProcessed()} ignoring request - no reply subject`,
376
+ );
377
+ }
378
+ }
379
+ }
380
+
381
+ // let's create two queue groups and a standalone subscriber
382
+ const conns = [];
383
+ conns.push(...await createService("echo", 3, "echo"));
384
+ conns.push(...await createService("other-echo", 2, "other-echo"));
385
+ conns.push(...await createService("standalone"));
386
+
387
+ const a: Promise<void | Error>[] = [];
388
+ conns.forEach((c) => {
389
+ a.push(c.closed());
390
+ });
391
+ await Promise.all(a);
392
+ ```
393
+
394
+ Run it and publish a request to the subject `echo` to see what happens.
395
+
396
+ ## Advanced Usage
397
+
398
+ ### Headers
399
+
400
+ NATS headers are similar to HTTP headers. Headers are enabled automatically if
401
+ the server supports them. Note that if you publish a message using headers, and
402
+ the server doesn't support them, an Error is thrown. Also note that even if you
403
+ are publishing a message with a header, it is possible for the recipient to not
404
+ support them.
405
+
406
+ ```javascript
407
+ import { connect, createInbox, Empty, headers } from "nats";
408
+
409
+ const nc = await connect(
410
+ {
411
+ servers: `demo.nats.io`,
412
+ },
413
+ );
414
+
415
+ const subj = createInbox();
416
+ const sub = nc.subscribe(subj);
417
+ (async () => {
418
+ for await (const m of sub) {
419
+ if (m.headers) {
420
+ for (const [key, value] of m.headers) {
421
+ console.log(`${key}=${value}`);
422
+ }
423
+ // reading/setting a header is not case sensitive
424
+ console.log("id", m.headers.get("id"));
425
+ }
426
+ }
427
+ })().then();
428
+
429
+ // headers always have their names turned into a canonical mime header key
430
+ // header names can be any printable ASCII character with the exception of `:`.
431
+ // header values can be any ASCII character except `\r` or `\n`.
432
+ // see https://www.ietf.org/rfc/rfc822.txt
433
+ const h = headers();
434
+ h.append("id", "123456");
435
+ h.append("unix_time", Date.now().toString());
436
+ nc.publish(subj, Empty, { headers: h });
437
+
438
+ await nc.flush();
439
+ await nc.close();
440
+ ```
441
+
442
+ ### No Responders
443
+
444
+ Requests can fail for many reasons. A common reason for a failure is the lack of
445
+ interest in the subject. Typically, these surface as a timeout error. If the
446
+ server is enabled to use headers, it will also enable a `no responders` feature.
447
+ If you send a request for which there's no interest, the request will be
448
+ immediately rejected:
449
+
450
+ ```javascript
451
+ const nc = await connect({
452
+ servers: `demo.nats.io`,
453
+ });
454
+
455
+ try {
456
+ const m = await nc.request("hello.world");
457
+ console.log(m.data);
458
+ } catch (err) {
459
+ switch (err.code) {
460
+ case ErrorCode.NoResponders:
461
+ console.log("no one is listening to 'hello.world'");
462
+ break;
463
+ case ErrorCode.Timeout:
464
+ console.log("someone is listening but didn't respond");
465
+ break;
466
+ default:
467
+ console.log("request failed", err);
468
+ }
469
+ }
470
+
471
+ await nc.close();
472
+ ```
473
+
474
+ ### Authentication
475
+
476
+ NATS supports many different forms of authentication:
477
+
478
+ - username/password
479
+ - token
480
+ - NKEYS
481
+ - client certificates
482
+ - JWTs
483
+
484
+ For user/password and token authentication, you can simply provide them as
485
+ `ConnectionOptions` - see `user`, `pass`, `token`. Internally these mechanisms
486
+ are implemented as an `Authenticator`. An `Authenticator` is simply a function
487
+ that handles the type of authentication specified.
488
+
489
+ Setting the `user`/`pass` or `token` options, simply initializes an
490
+ `Authenticator` and sets the username/password.
491
+
492
+ ```javascript
493
+ // if the connection requires authentication, provide `user` and `pass` or
494
+ // `token` options in the NatsConnectionOptions
495
+ import { connect } from "nats";
496
+
497
+ const nc1 = await connect({
498
+ servers: "127.0.0.1:4222",
499
+ user: "jenny",
500
+ pass: "867-5309",
501
+ });
502
+ const nc2 = await connect({ port: 4222, token: "t0pS3cret!" });
503
+ ```
504
+
505
+ #### Authenticators
506
+
507
+ NKEYs and JWT authentication are more complex, as they cryptographically respond
508
+ to a server challenge.
509
+
510
+ Because NKEY and JWT authentication may require reading data from a file or an
511
+ HTTP cookie, these forms of authentication will require a bit more from the
512
+ developer to activate them. However, the work is related to accessing these
513
+ resources varies depending on the platform.
514
+
515
+ After the credential artifacts are read, you can use one of these functions to
516
+ create the authenticator. You then simply assign it to the `authenticator`
517
+ property of the `ConnectionOptions`:
518
+
519
+ - `nkeyAuthenticator(seed?: Uint8Array | (() => Uint8Array)): Authenticator`
520
+ - `jwtAuthenticator(jwt: string | (() => string), seed?: Uint8Array | (()=> Uint8Array)): Authenticator`
521
+ - `credsAuthenticator(creds: Uint8Array): Authenticator`
522
+
523
+ The first two options provide the ability to specify functions that return the
524
+ desired value. This enables dynamic environments such as a browser where values
525
+ accessed by fetching a value from a cookie.
526
+
527
+ Here's an example:
528
+
529
+ ```javascript
530
+ // read the creds file as necessary, in the case it
531
+ // is part of the code for illustration purposes
532
+ const creds = `-----BEGIN NATS USER JWT-----
533
+ eyJ0eXAiOiJqdSDJB....
534
+ ------END NATS USER JWT------
535
+
536
+ ************************* IMPORTANT *************************
537
+ NKEY Seed printed below can be used sign and prove identity.
538
+ NKEYs are sensitive and should be treated as secrets.
539
+
540
+ -----BEGIN USER NKEY SEED-----
541
+ SUAIBDPBAUTW....
542
+ ------END USER NKEY SEED------
543
+ `;
544
+
545
+ const nc = await connect(
546
+ {
547
+ port: 4222,
548
+ authenticator: credsAuthenticator(new TextEncoder().encode(creds)),
549
+ },
550
+ );
551
+ ```
552
+
553
+ The node client supports the ability to verify the tls connection if client
554
+ certificates are specified as ConnectionOptions:
555
+
556
+ ```javascript
557
+ tlsOptions = {
558
+ keyFile: "./test/certs/client-key.pem",
559
+ certFile: "./test/certs/client-cert.pem",
560
+ caFile: "./test/certs/ca.pem",
561
+ };
562
+ nc = await connect({ tls: tlsOptions });
563
+ ```
564
+
565
+ ### Flush
566
+
567
+ Flush sends a PING to the server. When the server responds with PONG you are
568
+ guaranteed that all pending data was sent and received by the server. Note
569
+ `ping()` effectively adds a server round-trip. All NATS clients handle their
570
+ buffering optimally, so `ping(): Promise<void>` shouldn't be used except in
571
+ cases where you are writing some sort of test.
572
+
573
+ ```javascript
574
+ nc.publish("foo");
575
+ nc.publish("bar");
576
+ await nc.flush();
577
+ ```
578
+
579
+ ### `PublishOptions`
580
+
581
+ When you publish a message you can specify some options:
582
+
583
+ - `reply` - this is a subject to receive a reply (you must set up a
584
+ subscription) before you publish.
585
+ - `headers` - a set of headers to decorate the message.
586
+
587
+ ### `SubscriptionOptions`
588
+
589
+ You can specify several options when creating a subscription:
590
+
591
+ - `max`: maximum number of messages to receive - auto unsubscribe
592
+ - `timeout`: how long to wait for the first message
593
+ - `queue`: the [queue group](#queue-groups) name the subscriber belongs to
594
+ - `callback`: a function with the signature
595
+ `(err: NatsError|null, msg: Msg) => void;` that should be used for handling
596
+ the message. Subscriptions with callbacks are NOT iterators.
597
+
598
+ #### Auto Unsubscribe
599
+
600
+ ```javascript
601
+ // subscriptions can auto unsubscribe after a certain number of messages
602
+ nc.subscribe("foo", { max: 10 });
603
+ ```
604
+
605
+ #### Timeout Subscriptions
606
+
607
+ ```javascript
608
+ // create subscription with a timeout, if no message arrives
609
+ // within the timeout, the function running the iterator with
610
+ // reject - depending on how you code it, you may need a
611
+ // try/catch block.
612
+ const sub = nc.subscribe("hello", { timeout: 1000 });
613
+ (async () => {
614
+ for await (const m of sub) {
615
+ }
616
+ })().catch((err) => {
617
+ if (err.code === ErrorCode.Timeout) {
618
+ console.log(`sub timed out!`);
619
+ } else {
620
+ console.log(`sub iterator got an error!`);
621
+ }
622
+ });
623
+ ```
624
+
625
+ ### `RequestOptions`
626
+
627
+ When making a request, there are several options you can pass:
628
+
629
+ - `timeout`: how long to wait for the response
630
+ - `headers`: optional headers to include with the message
631
+ - `noMux`: create a new subscription to handle the request. Normally a shared
632
+ subscription is used to receive response messages.
633
+ - `reply`: optional subject where the reply should be sent.
634
+
635
+ #### `noMux` and `reply`
636
+
637
+ Under the hood, the request API simply uses a wildcard subscription to handle
638
+ all requests you send.
639
+
640
+ In some cases, the default subscription strategy doesn't work correctly. For
641
+ example, a client may be constrained by the subjects where it can receive
642
+ replies.
643
+
644
+ When `noMux` is set to `true`, the client will create a normal subscription for
645
+ receiving the response to a generated inbox subject before the request is
646
+ published. The `reply` option can be used to override the generated inbox
647
+ subject with an application provided one. Note that setting `reply` requires
648
+ `noMux` to be `true`:
649
+
650
+ ```javascript
651
+ const m = await nc.request(
652
+ "q",
653
+ Empty,
654
+ { reply: "bar", noMux: true, timeout: 1000 },
655
+ );
656
+ ```
657
+
658
+ ### Draining Connections and Subscriptions
659
+
660
+ Draining provides for a graceful way to unsubscribe or close a connection
661
+ without losing messages that have already been dispatched to the client.
662
+
663
+ You can drain a subscription or all subscriptions in a connection.
664
+
665
+ When you drain a subscription, the client sends an `unsubscribe` protocol
666
+ message to the server followed by a `flush`. The subscription handler is only
667
+ removed after the server responds. Thus, all pending messages for the
668
+ subscription have been processed.
669
+
670
+ Draining a connection, drains all subscriptions. However, when you drain the
671
+ connection it becomes impossible to make new subscriptions or send new requests.
672
+ After the last subscription is drained, it also becomes impossible to publish a
673
+ message. These restrictions do not exist when just draining a subscription.
674
+
675
+ ### Lifecycle/Informational Events
676
+
677
+ Clients can get notification on various event types:
678
+
679
+ - `Events.Disconnect`
680
+ - `Events.Reconnect`
681
+ - `Events.Update`
682
+ - `Events.LDM`
683
+ - `Events.Error`
684
+
685
+ The first two fire when a client disconnects and reconnects respectively. The
686
+ payload will be the server where the event took place.
687
+
688
+ The `UPDATE` event notifies whenever the client receives a cluster configuration
689
+ update. The `ServersChanged` interface provides two arrays: `added` and
690
+ `deleted` listing the servers that were added or removed.
691
+
692
+ The `LDM` event notifies that the current server has signaled that it is running
693
+ in _Lame Duck Mode_ and will evict clients. Depending on the server
694
+ configuration policy, the client may want to initiate an ordered shutdown, and
695
+ initiate a new connection to a different server in the cluster.
696
+
697
+ The `ERROR` event notifies you of async errors that couldn't be routed in a more
698
+ precise way to your client. For example, permission errors for a subscription or
699
+ request, will properly be reported by the subscription or request. However,
700
+ permission errors on publish will be reported via the status mechanism.
701
+
702
+ ```javascript
703
+ const nc = await connect();
704
+ (async () => {
705
+ console.info(`connected ${nc.getServer()}`);
706
+ for await (const s of nc.status()) {
707
+ console.info(`${s.type}: ${s.data}`);
708
+ }
709
+ })().then();
710
+
711
+ nc.closed()
712
+ .then((err) => {
713
+ console.log(
714
+ `connection closed ${err ? " with error: " + err.message : ""}`,
715
+ );
716
+ });
717
+ ```
718
+
719
+ Be aware that when a client closes, you will need to wait for the `closed()`
720
+ promise to resolve. When it resolves, the client is done and will not reconnect.
721
+
722
+ ### Async vs. Callbacks
723
+
724
+ Previous versions of the JavaScript NATS clients specified callbacks for message
725
+ processing. This required complex handling logic when a service required
726
+ coordination of operations. Callbacks are an inversion of control anti-pattern.
727
+
728
+ The async APIs trivialize complex coordination and makes your code easier to
729
+ maintain. With that said, there are some implications:
730
+
731
+ - Async subscriptions buffer inbound messages.
732
+ - Subscription processing delays until the runtime executes the promise related
733
+ microtasks at the end of an event loop.
734
+
735
+ In a traditional callback-based library, I/O happens after all data yielded by a
736
+ read in the current event loop completes processing. This means that callbacks
737
+ are invoked as part of processing. With async, the processing is queued in a
738
+ microtask queue. At the end of the event loop, the runtime processes the
739
+ microtasks, which in turn resumes your functions. As expected, this increases
740
+ latency, but also provides additional liveliness.
741
+
742
+ To reduce async latency, the NATS client allows processing a subscription in the
743
+ same event loop that dispatched the message. Simply specify a `callback` in the
744
+ subscription options. The signature for a callback is
745
+ `(err: (NatsError|null), msg: Msg) => void`. When specified, the subscription
746
+ iterator will never yield a message, as the callback will intercept all
747
+ messages.
748
+
749
+ Note that `callback` likely shouldn't even be documented, as likely it is a
750
+ workaround to an underlying application problem where you should be considering
751
+ a different strategy to horizontally scale your application, or reduce pressure
752
+ on the clients, such as using queue workers, or more explicitly targeting
753
+ messages. With that said, there are many situations where using callbacks can be
754
+ more performant or appropriate.
755
+
756
+ ## Connection Options
757
+
758
+ The following is the list of connection options and default values.
759
+
760
+ | Option | Default | Description |
761
+ | ----------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
762
+ | `authenticator` | none | Specifies the authenticator function that sets the client credentials. |
763
+ | `debug` | `false` | If `true`, the client prints protocol interactions to the console. Useful for debugging. |
764
+ | `ignoreClusterUpdates` | `false` | If `true` the client will ignore any cluster updates provided by the server. |
765
+ | `ignoreAuthErrorAbort` | `false` | Prevents client connection aborts if the client fails more than twice in a row with an authentication error |
766
+ | `inboxPrefix` | `"_INBOX"` | Sets de prefix for automatically created inboxes - `createInbox(prefix)` |
767
+ | `maxPingOut` | `2` | Max number of pings the client will allow unanswered before raising a stale connection error. |
768
+ | `maxReconnectAttempts` | `10` | Sets the maximum number of reconnect attempts. The value of `-1` specifies no limit. |
769
+ | `name` | | Optional client name - recommended to be set to a unique client name. |
770
+ | `noAsyncTraces` | `false` | When `true` the client will not add additional context to errors associated with request operations. Setting this option to `true` will greatly improve performance of request/reply and JetStream publishers. |
771
+ | `noEcho` | `false` | Subscriptions receive messages published by the client. Requires server support (1.2.0). If set to true, and the server does not support the feature, an error with code `NO_ECHO_NOT_SUPPORTED` is emitted, and the connection is aborted. Note that it is possible for this error to be emitted on reconnect when the server reconnects to a server that does not support the feature. |
772
+ | `noRandomize` | `false` | If set, the order of user-specified servers is randomized. |
773
+ | `pass` | | Sets the password for a connection. |
774
+ | `pedantic` | `false` | Turns on strict subject format checks. |
775
+ | `pingInterval` | `120000` | Number of milliseconds between client-sent pings. |
776
+ | `port` | `4222` | Port to connect to (only used if `servers` is not specified). |
777
+ | `reconnect` | `true` | If false, client will not attempt reconnecting. |
778
+ | `reconnectDelayHandler` | Generated function | A function that returns the number of millis to wait before the next connection to a server it connected to `()=>number`. |
779
+ | `reconnectJitter` | `100` | Number of millis to randomize after `reconnectTimeWait`. |
780
+ | `reconnectJitterTLS` | `1000` | Number of millis to randomize after `reconnectTimeWait` when TLS options are specified. |
781
+ | `reconnectTimeWait` | `2000` | If disconnected, the client will wait the specified number of milliseconds between reconnect attempts. |
782
+ | `servers` | `"localhost:4222"` | String or Array of hostport for servers. |
783
+ | `timeout` | 20000 | Number of milliseconds the client will wait for a connection to be established. If it fails it will emit a `connection_timeout` event with a NatsError that provides the hostport of the server where the connection was attempted. |
784
+ | `tls` | TlsOptions | A configuration object for requiring a TLS connection (not applicable to nats.ws). |
785
+ | `token` | | Sets a authorization token for a connection. |
786
+ | `user` | | Sets the username for a connection. |
787
+ | `verbose` | `false` | Turns on `+OK` protocol acknowledgements. |
788
+ | `waitOnFirstConnect` | `false` | If `true` the client will fall back to a reconnect mode if it fails its first connection attempt. |
789
+
790
+ ### TlsOptions
791
+
792
+ | Option | Default | Description |
793
+ | ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
794
+ | `caFile` | | CA certificate filepath |
795
+ | `ca` | | CA certificate |
796
+ | `certFile` | | Client certificate file path |
797
+ | `cert` | | Client certificate |
798
+ | `keyFile` | | Client key file path |
799
+ | `key` | | Client key |
800
+ | `handshakeFirst` | false | Connects to the server directly as TLS rather than upgrade the connection. Note that the server must be configured accordingly. |
801
+
802
+ In some Node and Deno clients, having the option set to an empty option,
803
+ requires the client have a secured connection.
804
+
805
+ ### Jitter
806
+
807
+ The settings `reconnectTimeWait`, `reconnectJitter`, `reconnectJitterTLS`,
808
+ `reconnectDelayHandler` are all related. They control how long before the NATS
809
+ client attempts to reconnect to a server it has previously connected.
810
+
811
+ The intention of the settings is to spread out the number of clients attempting
812
+ to reconnect to a server over a period of time, and thus preventing a
813
+ ["Thundering Herd"](https://docs.nats.io/developing-with-nats/reconnect/random).
814
+
815
+ The relationship between these are:
816
+
817
+ - If `reconnectDelayHandler` is specified, the client will wait the value
818
+ returned by this function. No other value will be taken into account.
819
+ - If the client specified TLS options, the client will generate a number between
820
+ 0 and `reconnectJitterTLS` and add it to `reconnectTimeWait`.
821
+ - If the client didn't specify TLS options, the client will generate a number
822
+ between 0 and `reconnectJitter` and add it to `reconnectTimeWait`.
823
+
824
+ ## JetStream
825
+
826
+ JetStream is the NATS persistence engine providing streaming, message, and
827
+ worker queues with At-Least-Once semantics.
828
+ [Support for JetStream is built-in](https://github.com/nats-io/nats.deno/blob/main/jetstream.md).
829
+
830
+ ## Service API
831
+
832
+ The service API allows you to
833
+ [easily build NATS services](https://github.com/nats-io/nats.deno/blob/main/services.md)
834
+ The services API is currently in beta functionality.
835
+
836
+ ## Contributing
837
+
838
+ The library shares client functionality with
839
+ [NATS.deno](https://github.com/nats-io/nats.deno). This means that both the
840
+ NATS.deno and NATS.js use the same exact code base, only differing on the
841
+ implementation of the `Transport`. This strategy greatly reduces the amount of
842
+ work required to develop and maintain the clients, as well as provide a
843
+ completely compatible API across all clients.
844
+
845
+ Currently, the base client implementation is the deno implementation. You can
846
+ take a look at it
847
+ [here](https://github.com/nats-io/nats.deno/tree/main/nats-base-client).
848
+
849
+ ## Supported Node Versions
850
+
851
+ Our support policy for Nodejs versions follows
852
+ [Nodejs release support](https://github.com/nodejs/Release). We will support and
853
+ build node-nats on even-numbered Nodejs versions that are current or in LTS.