# NATS Messaging Client

# NATS Messaging Client

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [pnpm-lock.yaml](pnpm-lock.yaml)
- [src/nats/__tests__/nats-client.test.ts](src/nats/__tests__/nats-client.test.ts)
- [src/nats/nats-client.ts](src/nats/nats-client.ts)

</details>



The `nats` module provides a singleton-based management layer for [NATS](https://nats.io/) connectivity. It facilitates connection sharing across a microservice, supports JetStream integration, and handles graceful teardown. To maintain a lightweight footprint, the SDK treats `nats` as an optional peer dependency, allowing services that do not require messaging to omit the library entirely [src/nats/nats-client.ts:7-10]().

## Architecture and Design

The `NatsClient` class follows a singleton pattern to ensure that only one connection to the NATS server is established and maintained per process [src/nats/nats-client.ts:11-13](). It provides lazy initialization, meaning the connection is only created when `getConnection()` is first invoked [src/nats/nats-client.ts:15-18]().

### NATS Client Data Flow

The following diagram illustrates the lifecycle of a NATS connection within the SDK, from initialization to JetStream access and final closure.

**NATS Connection Lifecycle**
```mermaid
sequenceDiagram
    participant Consumer as "Service Logic"
    participant SDK as "NatsClient"
    participant ENV as "process.env.NATS_URL"
    participant NATS as "NATS Server"

    Consumer->>SDK: getConnection()
    alt nc is null
        SDK->>ENV: Read NATS_URL
        ENV-->>SDK: URL (or default 127.0.0.1:4222)
        SDK->>NATS: connect({ servers: natsUrl })
        NATS-->>SDK: NatsConnection
        SDK->>SDK: nc.jetstream()
    else nc exists
        SDK-->>Consumer: Return existing nc
    end
    SDK-->>Consumer: NatsConnection

    Consumer->>SDK: getJetStream()
    SDK-->>Consumer: JetStreamClient

    Consumer->>SDK: close()
    SDK->>NATS: nc.close()
    SDK->>SDK: Reset nc/js to null
```
**Sources:** [src/nats/nats-client.ts:15-38]()

## Implementation Details

### Connection Management
The client uses the `NATS_URL` environment variable to locate the server. If this variable is not provided, it defaults to `nats://127.0.0.1:4222` [src/nats/nats-client.ts:17-18]().

| Method | Role | Behavior |
| :--- | :--- | :--- |
| `getConnection()` | Lazy Initializer | Checks if `nc` exists; if not, calls `connect()` and initializes JetStream [src/nats/nats-client.ts:15-22](). |
| `getJetStream()` | Accessor | Returns the initialized `JetStreamClient`. Throws an error if `getConnection()` hasn't been called [src/nats/nats-client.ts:24-29](). |
| `close()` | Teardown | Closes the active connection and nullifies internal references to allow for clean re-initialization [src/nats/nats-client.ts:31-38](). |

### Peer Dependency Model
The SDK is designed so that `nats` is not a hard requirement. This is achieved by:
1.  Listing `nats` under `devDependencies` in the SDK itself for testing [pnpm-lock.yaml:14-16]().
2.  Expecting the consumer to provide the `nats` package if they import the `nats` module from the SDK [src/nats/nats-client.ts:7-9]().
3.  Ensuring no other SDK modules (like `config` or `http`) import from the `nats` directory.

## Code Entity Mapping

The following diagram maps the logical messaging components to the specific TypeScript entities in the `nats` module.

**Messaging Entity Map**
```mermaid
graph TD
    subgraph "NATS Module"
        NC["class NatsClient"]
        GC["static getConnection()"]
        GJS["static getJetStream()"]
        CL["static close()"]
    end

    subgraph "External Dependencies"
        NATS_LIB["peer-dep: nats"]
        ENV_VAR["process.env.NATS_URL"]
    end

    NC --> GC
    NC --> GJS
    NC --> CL
    GC -.->|uses| NATS_LIB
    GC -.->|reads| ENV_VAR
    GJS -.->|returns| JS_TYPE["JetStreamClient"]
    GC -.->|returns| NC_TYPE["NatsConnection"]
```
**Sources:** [src/nats/nats-client.ts:1-39]()

## Testing and Singleton Resets

Because `NatsClient` maintains a static state, tests must explicitly reset the singleton to ensure isolation. The test suite utilizes `vi.mock("nats", ...)` to intercept connection attempts and `NatsClient.close()` within `beforeEach` hooks to clear the internal state between test cases [src/nats/__tests__/nats-client.test.ts:4-20]().

**Key Test Scenarios:**
*   **Singleton Verification:** Ensuring multiple calls to `getConnection()` return the exact same instance [src/nats/__tests__/nats-client.test.ts:26-31]().
*   **Initialization Guard:** Verifying that `getJetStream()` throws an error if the connection has not been established yet [src/nats/__tests__/nats-client.test.ts:33-35]().
*   **Reconnection Logic:** Confirming that calling `close()` allows a subsequent `getConnection()` to initiate a fresh connection [src/nats/__tests__/nats-client.test.ts:43-49]().

**Sources:** [src/nats/__tests__/nats-client.test.ts:1-54](), [src/nats/nats-client.ts:1-39]()

---
