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

# Nest.js SDK

> Integrate FlagSync feature flags into Nest.js applications.

## Overview

The `@flagsync/nest-sdk` integrates into [Nest.js](https://nestjs.com/) applications for server-side feature management and event tracking—ideal for backend services and APIs.

<CardGroup cols={2}>
  <Card title="npm" icon="npm" href="https://www.npmjs.com/package/@flagsync/nestjs-sdk">
    Latest version available on npm
  </Card>

  <Card title="GitHub" icon="github" href="https://github.com/flagsync/nestjs-sdk/">
    Explore the source code on GitHub
  </Card>
</CardGroup>

## Installation

Install the SDK with your preferred package manager:

<CodeGroup>
  ```bash npm theme={null}
  npm install @flagsync/nestjs-sdk
  npm install -D @flagsync/cli
  ```

  ```bash yarn theme={null}
  yarn add @flagsync/nestjs-sdk
  yarn add -D @flagsync/cli
  ```

  ```bash pnpm theme={null}
  pnpm add @flagsync/nestjs-sdk
  pnpm add -D @flagsync/cli
  ```
</CodeGroup>

## Quickstart

A basic example of using the SDK in a Nest.js application—set up the SDK in two steps:

<Steps>
  <Step title="Configure the Module">
    Import `FlagSyncModule` in your `app.module.ts`:

    ```typescript app.module.ts theme={null}
    import { Module } from '@nestjs/common';
    import { FlagSyncModule } from '@flagsync/nestjs-sdk';

    @Module({
      imports: [
        FlagSyncModule.forRoot({
          sdkKey: 'your-sdk-key',
          // other configuration options
        }),
      ],
    })
    export class AppModule {}
    ```
  </Step>

  <Step title="Inject the Client">
    Use `@InjectFlagSync()` to inject the client into a [Provider](https://docs.nestjs.com/providers) (e.g., service, controller, etc.):

    ```typescript app.controller.ts theme={null}
    import { Controller, Get } from '@nestjs/common';
    import { FsClient, InjectFlagSync } from '@flagsync/nestjs-sdk';
    import { UserContext } from '@app/context.decorator';

    @Controller()
    export class AppController {
      constructor(@InjectFlagSync() private client: FsClient) {}

      @Get()
      async getMyFlag(@UserContext() context) {
        return this.client.flag(context, 'my-first-kill-switch');
      }
    }
    ```
  </Step>
</Steps>

## Initialization

<Warning>
  The SDK leverages the `onModuleDestroy` lifecycle event to automatically destroy the `FsClient` on application shutdown, releasing connections to FlagSync’s servers.

  To ensure proper cleanup, call `enableShutdownHooks` during application startup. See the Nest.js documentation for [Application Shutdown](https://docs.nestjs.com/fundamentals/lifecycle-events#application-shutdown)
</Warning>

### Get Your SDK Key

Find your server-side SDK key in your [workspace settings](https://www.flagsync.com/dashboard/settings/organization/workspaces/). Keep server-side keys private to protect flag rules.

### Initialize the SDK

Initialize the SDK in your Nest.js application using one of these methods:

#### Synchronous Configuration

Configure the SDK synchronously with `forRoot`:

```typescript theme={null}
import { Module } from '@nestjs/common';
import { FlagSyncModule } from '@flagsync/nestjs-sdk';

@Module({
  imports: [
    FlagSyncModule.forRoot({
      sdkKey: 'your-sdk-key',
    }),
  ],
  ...
})
export class AppModule {}
```

#### Asynchronous Configuration

Configure the SDK asynchronously with `forRootAsync`, ideal for dynamic configuration with `ConfigService`:

```typescript theme={null}
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { FlagSyncModule } from '@flagsync/nestjs-sdk';

@Module({
  imports: [
    ConfigModule.forRoot({
      cache: true,
      isGlobal: true,
    }),
    FlagSyncModule.forRootAsync({
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        return {
          sdkKey: configService.get('FLAGSYNC_SDK_KEY'),
        };
      },
    }),
  ],
  ...
})
export class AppModule {}
```

### Wait for Readiness

The SDK initializes automatically during module setup—no need to manually wait for readiness with the injected `FsClient`.

## User Context Decorator

Simplify user context access by creating a [custom decorator](https://docs.nestjs.com/custom-decorators#decorator-composition) that returns an `FsUserContext` object. Pass this object to `flag()` and `track()` functions.

This decorator enables passing `@UserContext()` as a parameter in controller methods.

<Tip>
  Refer to the Nest.js documentation for [Authentication](https://docs.nestjs.com/security/authentication) to learn about `AuthGuards`, which can append `user` incoming requests.
</Tip>

<CodeGroup>
  ```typescript @app/context.decorator.ts theme={null}
  import { createParamDecorator, ExecutionContext } from '@nestjs/common';
  import { FsUserContext } from '@flagsync/nestjs-sdk';

  export const UserContext = createParamDecorator(
    (_, ctx: ExecutionContext): FsUserContext => {
      const request = ctx.switchToHttp().getRequest();

      // Populated via middleware or an AuthGuard
      // Or use request.cookies, etc.
      const user = request.user;

      return {
        key: user.userId,
        attributes: {
          jobTitle: user.role,
          region: request.headers['x-region'] ?? 'Unknown',
        },
      };
    },
  );
  ```

  ```typescript app.controller.ts theme={null}
  import { Controller, Get } from '@nestjs/common';
  import { FsClient, InjectFlagSync } from '@flagsync/nestjs-sdk';
  import { UserContext } from '@app/context.decorator';

  @Controller()
  export class AppController {
    constructor(@InjectFlagSync() private client: FsClient) {}

    @Get()
    async getMyFlag(@UserContext() context) {
      return this.client.flag(context, 'my-first-kill-switch');
    }
  }
  ```
</CodeGroup>

<Warning>
  Ensure the `key` in `FsUserContext` is unique and persistent for accurate MAU tracking and consistent flag evaluations. See [User Context Best Practices](/sdks/overview#user-context-best-practices) for details.
</Warning>

## Usage

### Evaluate Flags

Evaluates a feature flag for a given user context. Applies targeting rules, rollouts, and defaults values.

```typescript theme={null}
client.flag<T>(ctx: FsUserContext, flagKey: string, defaultValue?: T): T
```

```typescript app.service.ts {10} theme={null}
import { Injectable } from '@nestjs/common';
import { InjectFlagSync, FsClient, FsUserContext } from '@flagsync/nestjs-sdk';

@Injectable()
export class AppService {
  constructor(@InjectFlagSync() private readonly client: FsClient) {}

  // ctx passed from the controller
  async doWork(ctx: FsUserContext) {
    const isEnabled = this.client.flag(ctx, 'feature-enabled', false);
    if (isEnabled) {
      // Feature is enabled
    }
  }
}
```

#### FlagSync CLI

When using the CLI, flag values are automatically inferred from the generated types:

```typescript theme={null}
client.flag(ctx, 'layout');              // Type: 'v1' | 'v2' | 'v3'
client.flag(ctx, 'killswitch');          // Type: boolean
client.flag(ctx, 'price-discount');      // Type: 0.1 | 0.2
client.flag(ctx, 'price-discount', 0.5); // ❌ TS Error: Invalid default value
client.flag(ctx, 'invalid-flag');        // ❌ TS Error: Invalid flag key
```

Without generated types, you **must** manually specify the type:

```typescript theme={null}
client.flag<'v1' | 'v2'>(ctx, 'layout');            // Type: 'v1' | 'v2'
client.flag<number>(ctx, 'price-discount');         // Type: number
client.flag<boolean>(ctx, 'enable-feature', false); // Type: boolean (defaultValue must be true or false)
client.flag(ctx, 'no-type')                         // Type: unknown
```

<Tip>
  An [Impression](/impressions/generating) is automatically registered when `flag()` is called.
</Tip>

### Track Events

Submit user actions with the `track` function:

<Tip>
  See [Events: Tracking](/events/tracking) to learn about numeric and property events.
</Tip>

```typescript checkout.service.ts {14, 23} theme={null}
import { Injectable } from '@nestjs/common';
import { InjectFlagSync, FsClient, FsUserContext } from '@flagsync/nestjs-sdk';
import { PurchaseRequest } from '@app/types';

@Injectable()
export class CheckoutService {
  constructor(
    @InjectFlagSync()
    private readonly client: FsClient,
  ) {}

  async handlePurchase(request: PurchaseRequest, context: FsUserContext) {
    // Property event
    this.client.track(context, 'purchase-request', null, request);

    // Time the operation
    const t0 = Date.now();
    const order = await this.makePurchase(request);
    const receipt = await this.sendOrder(order)
    const t1 = Date.now()

    // Numeric event
    this.client.track(context, 'purchase-duration', t1 - t0);
  }
}
```

### SDK Event Listeners

The SDK emits these events for SDK lifecycle management:

* `SDK_UPDATE`: Emitted when flags are updated
* `SDK_READY`: Emitted when the SDK is ready
* `ERROR`: Emitted when an error occurs during initialization

```typescript theme={null}
import { FsEvent } from '@flagsync/js-sdk';

// Flag updates
this.client.on(FsEvent.SDK_UPDATE, () => {
  this.logger.log(`Flags updated at ${new Date().toISOString()}`)
});
```

<Note>
  `SDK_UPDATE` does not fire if syncing is disabled.
</Note>

## Configuration

Configure the SDK with the `FsConfig` interface:

```typescript theme={null}
export interface FsConfig {
  sdkKey: string;
  sync?: {
    type?: 'ws' | 'sse' | 'poll' | 'off'; // Optional: Sync strategy (default: 'ws')
    pollRateInSec?: number;               // Optional: Polling interval in seconds
  };
  tracking?: {
    impressions?: {
      maxQueueSize: number;           // Required: Max impressions queue size
      pushRateInSec: number;          // Required: Impressions push rate (seconds)
    };
    events?: {
      maxQueueSize: number;           // Required: Max events queue size
      pushRateInSec: number;          // Required: Events push rate (seconds)
    };
  };
  urls?: {
    ws?: string;                      // Optional: WebSocket sync URL
    sse?: string;                     // Optional: SSE sync URL
    flags?: string;                   // Optional: Flag fetch URL
    events?: string;                  // Optional: Events & impressions URL
  };
  logger?: Partial<ILogger>;          // Optional: Custom logger
  logLevel?: LogLevel;                // Optional: Logging level
  metadata?: Record<string, any>;     // Optional: Additional metadata
}
```

### Custom Attributes

* Define user attributes for targeting in [Flags: User Segments](/flags/targeting-and-rollouts#user-segments).
* See [User Context Decorator](#user-context-decorator) for the recommended approach to building the context.

```typescript theme={null}
const context: FsUserContext = {
  key: 'user-123',
  attributes: {
    plan: 'premium',
    country: 'US',
    userType: 'enterprise'
  }
}
```

<Warning>
  Ensure the `key` in `FsUserContext` is unique and persistent for accurate MAU tracking and consistent flag evaluations. See [User Context Best Practices](/sdks/overview#user-context-best-practices) for details.
</Warning>

### Flag Syncing

Configure flag update strategies with the `sync` object: `ws`, `sse`, `poll`, or `off`.

<Tip>
  By default, flag updates propagate in milliseconds over a WebSocket connection, ensuring the latest values are used in evaluations.
</Tip>

<Steps>
  <Step title="WebSocket (Default)">
    Stream updates over a WebSocket connection—flag updates are reevaluated on the server and pushed to the client in real time:

    ```typescript theme={null}
    FlagSyncModule.forRoot({
      sdkKey: 'your-sdk-key',
      sync: {
        type: 'ws' // Default
      }
    });
    ```
  </Step>

  <Step title="SSE">
    Stream updates via server-sent events (SSE):

    ```typescript theme={null}
    FlagSyncModule.forRoot({
      sdkKey: 'your-sdk-key',
      sync: {
        type: 'sse'
      }
    });
    ```
  </Step>

  <Step title="Polling">
    Poll the server on an interval:

    ```typescript theme={null}
    FlagSyncModule.forRoot({
      sdkKey: 'your-sdk-key',
      sync: {
        type: 'poll',
        pollRateInSec: 60
      }
    });
    ```
  </Step>

  <Step title="Off">
    Disable syncing:

    ```typescript theme={null}
    FlagSyncModule.forRoot({
      sdkKey: 'your-sdk-key',
      sync: {
        type: 'off'
      }
    });
    ```
  </Step>
</Steps>

### Bootstrapping

Initialize the SDK with a set of bootstrap flags:

```typescript theme={null}
FlagSyncModule.forRoot({
  sdkKey: 'your-sdk-key',
  bootstrap: {
    'my-feature': true,
    'other-feature': false,
  },
});
```

## Best Practices

* Select a sync strategy (`ws`/`sse`/`poll`/`off`) based on your application’s needs.
* Create a [User Context Decorator](#user-context-decorator) for simplified context management.
* Add user attributes for targeted feature rollouts.

## Environment Variables

Set the following environment variable:

* `FLAGSYNC_SDK_KEY`: Your server-side FlagSync SDK key (required)
