Appearance
Feature flags
What are feature flags?
A great explanation can be found in this article. Really worth a read!
What are providers?
A provider is the external service, library, or system responsible for managing and evaluating the state of all feature flags in an application.
You provide evaluation context to this provider to determine the state of a feature flag. This evaluation can, for example, contain the uuid or countryName of the user.
We make use of service called FeatBit, since it's open source and includes A/B Testing, Canary Launches & analytics.
Using feature flags
Adding a new feature flag
- Define the feature flag in the
FeatureFlagenum.
typescript
export enum FeatureFlag {
SOME_FEATURE = "some-feature",
ANOTHER_FEATURE = "another-feature",
}- Define the type of the feature flag in the
FeatureFlagTypesinterface.
typescript
export interface FeatureFlagTypes {
[FeatureFlag.SOME_FEATURE]: boolean;
[FeatureFlag.ANOTHER_FEATURE]: string;
}- Define the default value in the
defaultFeatureFlagValuesmap.
typescript
const defaultFeatureFlagValues: { [K in FeatureFlag]: FeatureFlagTypes[K] } = {
[FeatureFlag.SOME_FEATURE]: false,
[FeatureFlag.ANOTHER_FEATURE]: "a",
};- Create the feature flag in FeatBit dashboard.

Evaluating
Restricting endpoints
When you want to restrict an endpoint based on a feature flag, you can use the @FeatureFlags() decorator.
WARNING
You can only use boolean feature flags with this decorator.
typescript
import { FeatureFlags } from "#src/modules/feature-flags/feature-flag.decorator.js";
import { FeatureFlag } from "#src/modules/feature-flags/feature-flag.enum.js";
export class SomeFeatureController {
@FeatureFlags(FeatureFlag.SOME_FEATURE)
async execute(input: string): Promise<void> {
// Your logic here
}
}Programmatically
If you want to evaluate feature flags programmatically.
typescript
import { Injectable } from "@nestjs/common";
import { EvaluateFeatureFlagUseCase } from "#src/modules/feature-flag/use-cases/evaluate-feature-flag/evaluate-feature-flag.use-case.js";
@Injectable()
export class SomeFeatureUseCase {
constructor(
private readonly evaluateFeatureFlag: EvaluateFeatureFlagUseCase
) {}
public async execute() {
const isFeatureEnabled = await this.evaluateFeatureFlag.execute(
FeatureFlag.SOME_FEATURE
);
// Your logic here
}
}Provider factory
In order to differentiate between multiple providers (e.g. email clients) based on the feature flag value, you can create a factory that will return the correct provider instance.
WARNING
Make sure you set the scope to Scope.REQUEST so that the provider will be instantiated each request, as the evaluation context is request specific.
typescript
@Module({
providers: [
{
provide: MailClient,
useFactory: mailClientFactory,
inject: [ConfigService, EvaluateFeatureFlagUseCase],
scope: Scope.REQUEST,
},
MailService,
],
exports: [MailService],
})
export class MailModule {}typescript
import { ConfigService } from "@nestjs/config";
import { MailClient } from "#src/modules/mail/clients/mail.client.js";
import { EvaluateFeatureFlagUseCase } from "#src/modules/feature-flag/use-cases/evaluate-feature-flag/evaluate-feature-flag.use-case.js";
export async function mailClientFactory(
configService: ConfigService,
evaluateFeatureFlag: EvaluateFeatureFlagUseCase
): Promise<MailClient> {
const flag = await evaluateFeatureFlag.getStringValue(
FeatureFlag.MAIL_PROVIDER
);
switch (flag) {
case MailProvider.SCALEWAY:
return new ScalewayMailClient(configService);
case MailProvider.SEND_GRID:
return new SendGridMailClient(configService);
default:
exhaustiveCheck(flag);
}
}Testing
When creating the testing module, we set the defaultProvider to use an InMemoryProvider (featureFlagsTestProvider), which allows us to mock the feature flag evaluations in our tests.
To mock a feature flag evaluation, you can do the following:
typescript
mock.method(InMemoryProvider.prototype, "resolveBooleanEvaluation", async () =>
Promise.resolve(false)
);