npm package: @flic-sdk/node-server
Description: Node.js Server SDK for Flic feature flags (local evaluation)
Latest version: 0.0.3

# @flic-sdk/node-server

Node.js server SDK for Flic feature flags. Designed for multi-user server environments.

## Architecture

```
┌─────────────────┐      ┌─────────────┐
│  Node.js Server │      │  Flic API   │
│                 │ SSE  │             │
│  node-server-sdk│◄─────│   configs   │
│                 │      │   (rules)   │
│  [local eval]   │      │             │
└─────────────────┘      └─────────────┘
```

**Local evaluation**: Downloads flag configs (rules) and evaluates locally. Zero latency per evaluation.

## Installation

```bash
npm install @flic-sdk/node-server
```

## Quick Start

```ts
import { FlicServer } from "@flic-sdk/node-server";

const flic = new FlicServer({
  apiKey: "flic_xxx",
  baseUrl: "https://api.flic.dev",
  project: "my-project",
  environment: "production",
});

await flic.init(); // Fetches configs, starts streaming

// Evaluate for any user - instant, no network call
const enabled = flic.isEnabled("new-feature", {
  key: "user-123",
  attributes: { plan: "pro" }
});

const theme = flic.getValue("theme", "light", { key: "user-456" });

// Graceful shutdown
await flic.close();
```

## API

### Constructor

```ts
new FlicServer({
  apiKey: string;           // Required - API key
  baseUrl: string;          // Required - API URL
  project: string;          // Required - Project slug
  environment: string;      // Required - Environment name
  pollingInterval?: number; // Fallback polling interval (default: 30000ms)
  streaming?: boolean | StreamConfig; // SSE for config updates (default: true)
  analytics?: boolean | AnalyticsConfig; // Impression tracking (default: true)
});
```

### Methods

| Method | Returns | Description |
|--------|---------|-------------|
| `init()` | `Promise<void>` | Fetch configs, start updates. **Must await before evaluating.** |
| `isEnabled(key, ctx?)` | `boolean` | Evaluate boolean flag for context |
| `getValue(key, default, ctx?)` | `T` | Evaluate value flag for context |
| `evaluate(key, ctx?)` | `EvaluatedFlag` | Full evaluation result |
| `getAllFlags()` | `Map<string, FlagConfig>` | Get cached flag configs |
| `close()` | `Promise<void>` | Stop updates, flush analytics |

### Context

Pass context per-call (different users per request):

```ts
type EvaluateContext = {
  key?: string;  // User ID for consistent rollouts
  attributes?: Record<string, unknown>; // Targeting attributes
};

// Each call can have different context
flic.isEnabled("feature", { key: "user-1", attributes: { country: "US" } });
flic.isEnabled("feature", { key: "user-2", attributes: { country: "DE" } });
```

## Why Sync Evaluation?

**This is the standard pattern** used by LaunchDarkly, Unleash, and other SDKs:

```
┌─────────────────────────────────────────────────────────────┐
│                    Lifecycle                                 │
├─────────────────────────────────────────────────────────────┤
│  init()  → Async. Fetches configs. Start SSE/polling.       │
│  isEnabled() → Sync. Reads from in-memory cache. ~0ms.      │
│  getValue() → Sync. Evaluates rules locally. ~0ms.          │
│  close() → Async. Cleanup, flush analytics.                 │
└─────────────────────────────────────────────────────────────┘
```

**Benefits:**
- **Zero latency**: No network call during request handling
- **No async/await noise**: Simple `if (flic.isEnabled(...))`
- **Consistent**: Same flag value during entire request
- **Resilient**: Works even if API is temporarily unreachable

## Resilience

**What happens if the connection fails?**

1. **Cached configs persist** - All rules remain in memory
2. **Auto reconnect** - SDK retries with exponential backoff
3. **Fallback to polling** - After max retries, switches to polling
4. **Evaluations keep working** - Using cached configs until reconnect

```ts
// This always works after init(), even if disconnected
flic.isEnabled("feature", { key: "user" }); // Uses cached rules
```

**What about stale data?**

- Configs are typically stable (don't change every second)
- SSE updates arrive in <100ms when connected
- Polling interval is configurable (default: 30s)
- For critical changes, use kill switches that default to "off"

## Express Example

```ts
import express from "express";
import { FlicServer } from "@flic-sdk/node-server";

const flic = new FlicServer({
  apiKey: process.env.FLIC_API_KEY,
  baseUrl: "https://api.flic.dev",
  project: "my-project",
  environment: "production",
});

await flic.init();

const app = express();

app.get("/api/checkout", (req, res) => {
  const userId = req.user.id;

  if (flic.isEnabled("new-checkout", { key: userId })) {
    return handleNewCheckout(req, res);
  }
  return handleLegacyCheckout(req, res);
});

// Graceful shutdown
process.on("SIGTERM", async () => {
  await flic.close();
  process.exit(0);
});
```

## Streaming Config

```ts
new FlicServer({
  // ...
  streaming: {
    enabled: true,
    reconnectDelay: 1000,      // Initial retry delay
    maxReconnectDelay: 30000,  // Max retry delay
    maxReconnectAttempts: 5,   // Before fallback to polling
    fallbackToPolling: true,
  },
});
```

## Analytics

Tracks all evaluations for analytics:

```ts
new FlicServer({
  // ...
  analytics: {
    enabled: true,
    flushInterval: 10000, // Batch send interval
    maxBatchSize: 100,    // Max events before immediate flush
  },
});
```

Disable with `analytics: false`.

## Client SDK vs Server SDK

| Aspect | Client SDK | Server SDK |
|--------|------------|------------|
| Runtime | Browser | Node.js |
| Users | Single user | Multiple users |
| Evaluation | Server-side | Local |
| Context | Set once at init | Per evaluation call |
| Cache | Evaluated results | Flag configs + rules |
| Latency | Network per init | Network per init only |

Use **client-sdk** for browsers. Use **node-server-sdk** for servers.

## TypeScript

```ts
import type {
  EvaluateContext,
  FlicServerConfig,
  EvaluatedFlag,
  FlagConfig
} from "@flic-sdk/node-server";
```