npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@pipelife-labs/nest-pennant

v1.0.5

Published

A simple and lightweight feature flag package exclusively designed for NestJS

Downloads

8

Readme

NestJS Pennant

A simple and light-weight feature flag package exclusively designed for NestJS

  • This purpose-built tool eliminates unnecessary complexity
  • allowing you to seamlessly roll out new application features with confidence
  • conduct A/B testing for interface designs
  • effortlessly complement a trunk-based development strategy
  • unlock a multitude of possibilities within your NestJS projects.

Installation

First, install PennantModule into your project using the npm:

npm install --save @pipelife-labs/nest-pennant

Register PennantModule to your application module

import { ConfigModule, ConfigService } from '@nestjs/config'
import { IPennantDefinitions, PennantModule } from '@pipelife-labs/nest-pennant'

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    PennantModule.forRootAsync({
      imports: [],
      definitions: {
        imports: [],
        useFactory: (config: ConfigService): IPennantDefinitions => {
          return {
            enabledFeatures: config
              .get<string>('FEATURES_FLAG_ENABLED', '')
              .split(',')
              .map((ft) => ft.trim()),
          }
        },
        inject: [ConfigService],
      },
      /**
      * In case you want to register some providers inside PennantModule
      * These providers will be also exported from PennantModule
      */
      otherProviders: [],
      /**
      * To make the PennantModule available throughout the entire app
      */
      global: true,
    }),
  ],
  ...
})
export class AppModule {}

Definitions

interface IPennantDefinitions {
  enabledFeatures: string[]
  getUserFromRequestHandler?: (
    req: unknown
  ) => string | undefined | Promise<string | undefined>
}
  • enabledFeatures: specifies all the enabled features. In most cases, we fetch them via environment variables.
  • getUserFromRequestHandler: used when you want to check the features that are specified for specific users (A/B testing). This hook helps us to get the currently logged-in user. By default, we have a built-in function to get the current user:
/**
* @see ./domain/helpers/get-user-from-request.helper.ts
*/

interface IUSerFromRequest {
  [x: string]: unknown

  id: string
}

export const defaultGetUserFromRequest = (req: any): string | undefined => {
  const user: IUSerFromRequest = req.user

  return user?.id
}

This default approach is compatible with Passport JWT strategy for both Express and Fastify frameworks.

Define your own handler to get user from request

PennantModule.forRootAsync({
  definitions: {
    imports: [],
    useFactory: (config: ConfigService): IPennantDefinitions => {
      return {
        enabledFeatures: config
          .get<string>('FEATURES_FLAG_ENABLED', '')
          .split(',')
          .map((feature) => feature.trim()),
        getUserFromRequestHandler: async (
          req: any
        ): Promise<string | undefined> => {
          // Put your updated logic here
          return req?.user?.id
        },
      }
    },
    inject: [ConfigService],
  },
  ...
})

Define the repository to get the enabled features

This approach will be helpful if you need to fetch the features list from other data sources such as Firebase, MongoDB, PostgreSQL, or external APIs.

import { Injectable } from '@nestjs/common'
import { FeatureFlagRepository } from '@pipelife-labs/nest-pennant'

@Injectable()
export class SampleFeatureFlagRepository implements FeatureFlagRepository {
  public async getFeatures(): Promise<string[]> {
    // Put your updated logic here
    return []
  }

  public async getFeaturesByUser(userId: string): Promise<string[]> {
    // Put your updated logic here
    return []
  }
}

Then you need to define it when register our PennantModule

PennantModule.forRootAsync({
  featureFlagRepository: {
    useFactory: () => {
      return new SampleFeatureFlagRepository()
    },
    inject: []
  },
  ...
})

Usages

FeaturesEnabledSimple decorator

It will only check the features passed to the package via the enabledFeatures from definitions options when you register the PennantModule.

Internally, it utilizes the SimpleFeaturesFlagService to check the features flag.

import { FeaturesEnabledSimple } from '@pipelife-labs/nest-pennant'

@Controller('test-3')
@FeaturesEnabledSimple(['FEATURE_6', 'FEATURE_7'])
export class Test3Controller {
  @Get()
  public index(): string {
    return 'Hello World!'
  }
}

@Controller('test-4')
export class Test4Controller {
  @Get()
  @FeaturesEnabledSimple(['FEATURE_6', 'FEATURE_7'])
  public index(): string {
    return 'Hello World!'
  }
}

FeaturesEnabled decorator

The fetched features from featureFlagRepository will then be merged with the features list in the enabledFeatures from the definitions option mentioned above.

Internally, it utilizes the FeaturesFlagService (which defined in REQUEST scope) to check the features flag.

import { FeaturesEnabled } from '@pipelife-labs/nest-pennant'

@Controller('test')
@FeaturesEnabled(['FEATURE_5'])
export class TestController {
  @Get()
  public index(): string {
    return 'Hello World!'
  }
}

@Controller('test-2')
export class Test2Controller {
  @Get()
  @FeaturesEnabled(['FEATURE_6', 'FEATURE_7'])
  public index(): string {
    return 'Hello World!'
  }
}

FeaturesEnabledForUser decorator

This approach functions similarly to FeaturesEnabled, but it verifies the features for the currently logged-in user.

Certainly, please make sure to check the getUserFromRequestHandler handler that was mentioned earlier.

import { FeaturesEnabled } from '@pipelife-labs/nest-pennant'

@Controller('test-5')
export class Test2Controller {
  @Get()
  @FeaturesEnabledForUser(['FEATURE_6', 'FEATURE_7'])
  public index(): string {
    return 'Hello World!'
  }
}

SimpleFeaturesFlagService & FeaturesFlagService services

You can also use the exposed services SimpleFeaturesFlagService and FeaturesFlagService from the package to perform logical checks as well.

import { SimpleFeaturesFlagService, FeaturesFlagService } from '@pipelife-labs/nest-pennant'

@Controller('test-6')
export class Test6Controller {
  public constructor(
    private readonly _simpleFeaturesFlagService: SimpleFeaturesFlagService,
    private readonly _featuresFlagService: FeaturesFlagService
  ) {}

  @Get()
  public index(): string {
    console.log(this._simpleFeaturesFlagService)
    console.log(this._featuresFlagService)
    return 'Hello World!'
  }
}

Note: FeaturesFlagService is initialized with a REQUEST scope.

Learn more about the NestJS Injection Scopes