nats.js

repository·main·Indexed 19 days ago

https://github.com/nats-io/nats.js

JavaScript/TypeScript clients for the NATS messaging system. The @nats-io/nats-core library (v3.4.0) implements base functionality including connection management, authentication, and the NATS protocol for publishing, subscribing, and request/reply patterns. It is designed to be runtime-agnostic, supporting Node.js, Deno, and Browser environments via specific transport modules or W3C WebSockets.

Tokens
31.4K
Snippets
103
Records
137
Agent score
64%

What's inside nats.js

  1. What is Obj and how does it relate to JetStream?

    main
    The obj module provides a materialized view of NATS JetStream. While JetStream uses streams to store and access data, obj presents a different API designed for interacting with data as an ObjectStore. This abstraction is often more familiar to application developers than raw stream manipulation.
  2. How JetStream streams and consumers work

    main

    JetStream is a persistence engine that provides streaming, message, and worker queues with At-Least-Once semantics.

    Streams

    JetStream stores messages in streams. A stream defines how messages are stored and sets limits (e.g., duration of persistence or maximum number of messages). To store a message, you publish it to a subject associated with a stream.

    Consumers

    Messages are replayed from a stream by consumers. A consumer configuration determines which messages are presented to a client, such as:

    • Filtering by specific sequence numbers or time ranges.
    • Filtering by specific subjects.
    • Defining acknowledgement policies (e.g., whether the server requires an ACK).
    • Controlling the rate at which messages are delivered.
  3. Configure Per-Key TTLs in KV

    main

    NATS Server 2.11+ supports automatic key removal via Time-To-Live (TTL). To use per-key TTLs, you must first create the bucket with the markerTTL option (minimum 1000ms).

    markerTTL defines how long the server keeps the 'tombstone' marker (the PURGE operation) in the bucket after a key is removed. This ensures watchers and get requests see the removal before the record disappears entirely.

    TTL Methods

    1. On Create: kv.create(key, value, "<duration>") sets the TTL for the initial write.
    2. On Purge: kv.purge(key, { ttl: "<duration>" }) sets the lifetime of the purge marker.

    Duration Format

    Durations are strings following the Go duration format (e.g., 2s, 2m, 1.5h). A bare number is treated as seconds. Minimum resolution is one second.

    Note: kv.put() does not support TTL. Using put() for expiry can cause issues with historical revision visibility.

    // 1. Create bucket with markerTTL (required for per-key TTL to work)
    const kv = await new Kvm(js).create("A", { markerTTL: 2_000 });
    
    // 2. Set TTL on creation (key expires in 5s)
    await kv.create("k", "hello", "5s");
    
    // 3. Or set TTL on purge (marker expires in 2s)
    await kv.purge("k", { ttl: "2s" });
  4. Best practices for using NATS in modern web frameworks

    main

    When using NATS in frameworks like React, Vue, or Svelte, follow these patterns:

    1. Avoid local state for connections: Do not wrap the NATS connection in component-local useState or useEffect. This causes the connection to re-run on every render.
    2. Use Singleton Promises: Create a module-scope singleton promise for the connection.
    3. Leverage Async Primitives: Consume the connection promise using the framework's built-in async handling, such as React <Suspense>, Vue <Suspense>, or Svelte {#await} blocks.
  5. Understand the new NATS.js modular architecture

    main

    The NATS JavaScript library has transitioned from a monolithic client to a modular ecosystem. Instead of one large package, functionality is split into specialized libraries to reduce dependency size and allow for independent versioning.

    To use specific features, you must now install and import the corresponding package:

    • Core (Pub/Sub, Request-Reply): @nats-io/nats-core (via transports like @nats-io/transport-node or @nats-io/transport-deno).
    • JetStream: @nats-io/jetstream (depends on @nats-io/nats-core).
    • Key-Value (KV): @nats-io/kv (depends on @nats-io/jetstream).
    • ObjectStore: @nats-io/obj (depends on @nats-io/jetstream).
    • Services: @nats-io/services (depends on @nats-io/nats-core).

    Transports like @nats-io/transport-node and @nats-io/transport-deno re-export @nats-io/nats-core APIs, so installing a transport provides access to core functionality automatically.

  6. Discover and monitor services

    main

    The framework automatically assigns a unique ID to every service instance. While multiple instances may share the same name, they will have unique IDs.

    You can discover running services by creating a monitoring client via svc.client(). This client allows you to perform operations like ping(), stats(), and info(). All these operations return iterators describing the services found. You can filter these operations by service name or a specific instance id.

    const m = svc.client();
    
    // Ping all services
    for await (const s of await m.ping()) {
      console.log(s.id);
    }
    
    // Get stats for all services
    await m.stats();
    
    // Get info for all services
    await m.info();
    
    // Filter stats by service name
    await m.stats("max");
    
    // Filter stats by a specific instance ID
    await m.stats("max", id);
  7. Distinguish between Core client and Orbit

    main

    NATS client functionality is organized into two distinct layers:

    Core client (nats.js)

    • Purpose: Direct, lightweight, and performance-oriented API mapping to nats-server capabilities.
    • Characteristics: High cross-client parity (matches Rust, Go, etc.), stable versioning, and conservative breaking changes.
    • Use when: You need protocol-level coverage (auth, TLS, reconnection) or standard NATS features (publish, subscribe, JetStream, Service API).

    Orbit (orbit.js)

    • Purpose: High-level, opinionated abstractions built on top of the core client.
    • Characteristics: JavaScript-idiomatic APIs, faster iteration/API churn, and per-module versioning.
    • Use when: You want helpers, sugar over core APIs, KV codecs, distributed counters, or experimental patterns.
  8. How the @nats-io/nats-core module works

    main

    The core module provides the fundamental NATS functionality for JavaScript clients, including:

    • Connection management, authentication, and lifecycle handling.
    • NATS protocol implementation for messaging (publish, subscribe, and request/reply).

    It is designed to be runtime-agnostic. While native transport modules (like node or deno) provide a connect function that returns a NatsConnection, the core module itself can be used as a dependency for building other NATS-compatible libraries (e.g., @nats-io/jetstream) without binding to a specific runtime.

  9. Acknowledge and manage JsMsg lifecycle

    main

    All JetStream messages are JsMsg instances, which wrap a standard NATS Msg. They provide metadata like seq (sequence) and redelivered status. You must manage the message lifecycle using one of the following methods:

    • ack(): Successfully processed the message.
    • nak(millis?): Failed to process; tells the server to resend. If millis is provided, the server waits that long before resending (requires server v2.7.1+).
    • working(): Informs the server you are still processing to prevent premature redelivery.
    • term(): Failed to process and instructs the server never to send this message to any consumer again.
  10. How to use NATS in modern web frameworks

    main

    When using NATS in frameworks like React, Next.js, or Vue, follow these best practices:

    1. Avoid local state for connections: Do not wrap the NATS connection in component-local useState or useEffect. This causes the connection to re-run on every render.
    2. Use a singleton promise: Create a module-scope singleton promise for the connection.
    3. Use async primitives: Consume the connection promise using the framework's built-in async resource handling, such as React <Suspense>, Vue <Suspense>, or Svelte {#await}.

    For Next.js, it is recommended to load the NATS client via next/dynamic with ssr: false because WebSockets only run in the browser.

  11. Use NATS in modern web frameworks (React, Vue, Svelte)

    main

    When using NATS in modern web frameworks, do not wrap the connection in component-local useState or useEffect. This can cause the connection to re-run on every render.

    Recommended Pattern: Create a module-scope singleton promise for the connection and consume it using the framework's built-in async-resource primitives, such as:

    • React <Suspense> or the use hook
    • Vue <Suspense>
    • Svelte {#await}
    • Next.js streaming and loading.tsx
  12. Understand Async vs Callback subscription models

    main

    NATS.js supports two ways to handle messages:

    Using for await (const m of sub) is the modern approach. It simplifies complex coordination and makes code easier to maintain.

    • Implication: Async subscriptions buffer inbound messages. Processing is queued in a microtask queue, which may increase latency but provides better liveliness.

    Callbacks

    By providing a callback in SubscriptionOptions, you use a traditional callback-based approach.

    • Implication: This allows processing a subscription in the same event loop that dispatched the message, reducing latency.
    • Note: When a callback is used, the subscription iterator will never yield messages; the callback intercepts everything.