> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js SDK - Caching Behaviour

> When does the Node.js SDK hit the network, snapshot atomicity, and ETag/304.

The SDK keeps every config / secret / flag in an **in-memory snapshot** that is replaced atomically as a whole on each sync. Reads never block on the network; only the periodic background timer does.

## When does the SDK hit the network

| Event                             | Result                                                                          |
| --------------------------------- | ------------------------------------------------------------------------------- |
| `NexusClient.create()`            | **Blocking** full sync. The function only resolves once the cache is populated. |
| Read within the TTL window        | **No network** - served from the in-memory cache                                |
| Read after TTL has elapsed        | Stale cache returned **immediately**; one background refresh fires in parallel  |
| Server returns `304 Not Modified` | TTL reset, no payload downloaded                                                |
| Multiple concurrent expirations   | Only **one** background sync runs at a time                                     |
| SSE event received                | Immediate full sync, regardless of TTL                                          |
| SSE stream connected              | Effective polling interval bumped to **60 s** as a safety net                   |

<Note>
  `evaluateFlags` and `evaluateAB` are **always live network calls** - they bypass the cache by design. Use them only when you need the most up-to-date result for a specific user/context.
</Note>

## The atomic snapshot guarantee

The snapshot is replaced as one immutable record. Two reads either see the same old state or the same new state - never a mix. This means you can iterate `getAllConfigs()` or `getAllFlags()` without worrying about an entry being added or removed mid-loop.

## ETag / 304

Every successful sync stores the server's `ETag`. The next sync sends `If-None-Match: <etag>`. If nothing has changed:

```
HTTP/1.1 304 Not Modified
```

...the SDK keeps the existing snapshot and resets the next-poll timer. **No payload transferred.**

When the etag changes, the new payload replaces the cache atomically.

## Background sync errors

Background syncs **never throw** to the application. If the network call fails:

1. The error is swallowed silently
2. The existing cache continues to be served
3. The next read after the TTL elapses retries automatically

This is intentional - a transient backend hiccup must not surface as a customer-facing error in your handler.

## File-type secrets

When the sync response contains file-type secrets, the SDK writes them to disk:

```
$os.tmpdir()/nexus-secrets/nexus-<key>-<hash>
```

Behaviour:

* The path is stable across syncs as long as the value is unchanged (the hash component is content-derived).
* When a value changes, a new file is written and the previous one is deleted.
* All temp files are removed when `client.close()` is called.

<Warning>
  If you cache `getSecretFilePath()` somewhere, that path may go stale after a sync - re-read it before each use.
</Warning>

## Cache lifetime

The cache lives as long as the `NexusClient` instance. Disposing the client (via `close()`) releases the memory and tears down the polling timer + SSE socket. Typical use is **one singleton client per process**, kept around for the entire lifetime.

## Diagnostics

| Property              | What it tells you                                                                                          |
| --------------------- | ---------------------------------------------------------------------------------------------------------- |
| `client.syncedAt`     | When was the last successful sync - useful for "last refreshed" UI / health endpoints                      |
| `client.streamStatus` | `'connected'` (SSE alive), `'fallback'` (3-strike out, polling only), `'disconnected'` (manual disconnect) |
| `client.keyType`      | `'sk'` or `'pk'` - confirms the SDK saw the right key prefix                                               |
