Return to blog

Live Feature Flags with AWS Systems Manager and Node.js

hero image for Live Feature Flags with AWS Systems Manager and Node.js

Live Feature Flags with AWS Systems Manager and Node.js - Wiktor Kowalski

What are feature flags? Feature flags, also known as feature toggles, are a technique in modern software development that enables developers to turn certain functionalities on or off without altering the codebase. This approach facilitates controlled testing, phased rollouts, and quickly responding to issues by enabling or disabling features in real time. By decoupling deployment and feature release, feature flags provide a flexible mechanism for managing and iterating software features in various environments.

Setup in AWS

For the sake of this article I'll assume you’ve set up your AWS account and AWS CLI, so we’ll skip that part. I’m also assuming you have created an empty Nest.js project and have basic knowledge about Nest.js.

Let’s start by creating a parameter:

aws ssm put-parameter --name "/FeatureFlags/FeatureA" --value "true" --type String

and then some additional ones:

aws ssm put-parameter --name "/FeatureFlags/FeatureB" --value "false" --type String
aws ssm put-parameter --name "/FeatureFlags/FeatureC" --value "true" --type String

and now let’s check one just to be sure:

aws ssm get-parameter --name "/FeatureFlags/FeatureA"

and list them all:

aws ssm describe-parameters --filters "Key=Name,Values=/FeatureFlags/"

That’s all we need to do for now in AWS, let’s move to code example.

Configuration

First, install the required package:

npm install --save @aws-sdk/client-ssm

and add a service class that will make sure feature flags have up to date values:

// /src/ssm.service.ts
import { Injectable, OnModuleInit } from "@nestjs/common";
import { SSMClient, GetParametersByPathCommand } from "@aws-sdk/client-ssm";

@Injectable()
export class SsmService implements OnModuleInit {
  #ssm: SSMClient;
  #featureFlags: FeatureFlags;

  constructor() {
    this.#ssm = new SSMClient();
    this.#featureFlags = {} as FeatureFlags;
  }

  async onModuleInit() {
    await this.loadFeatureFlags();
    setInterval(() => this.loadFeatureFlags(), 5000);
  }

  private async loadFeatureFlags() {
    const command = new GetParametersByPathCommand({
      Path: "/FeatureFlags",
    });
    const response = await this.#ssm.send(command);
    response.Parameters.forEach(param => {
      const key = param.Name.split("/").pop();
      this.#featureFlags[key] = param.Value === "true";
    });
  }

  get featureFlags() {
    return this.#featureFlags;
  }
}

export type FeatureFlags = {
  featureA: boolean;
  featureB: boolean;
  featureC: boolean;
};

Keep in mind that API calls to AWS SSM might not be free, especially if you have Advanced Parameters and/or need access to High Throughput API. Remember to adjust the loadFeatureFlags() interval to more suitable value if that's the case. You can learn more about this in the Parameter Store Pricing page

That takes care of the configuration.

Usage

Now the SsmService is available to be passed to the controller where we can use FeatureFlags.

// src/feature-flags/feature-flags.controller.ts
import { Controller, Get } from "@nestjs/common";
import { SsmService } from "src/ssm.service";

@Controller("feature-flags")
export class FeatureFlagsController {
  constructor(private ssmService: SsmService) {}

  @Get()
  getFeatureFlags() {
    return this.ssmService.featureFlags;
  }
}

Remember to add everything necessary to app.module.ts.

import { Module } from "@nestjs/common";
import { FeatureFlagsController } from "./feature-flags/feature-flags.controller";
import { SsmService } from "./ssm.service";

@Module({
  imports: [],
  controllers: [FeatureFlagsController],
  providers: [SsmService],
})
export class AppModule {}

Now the GET /feature-flags endpoint returns:

{
  "FeatureA": true,
  "FeatureB": false,
  "FeatureC": true
}

Now let’s get to the magic part.

Live reload of config values

While keeping the app running, let’s change the parameter values:

aws ssm put-parameter --name "/FeatureFlags/FeatureA" --value "false" --type String --overwrite
aws ssm put-parameter --name "/FeatureFlags/FeatureB" --value "true" --type String --overwrite
aws ssm put-parameter --name "/FeatureFlags/FeatureC" --value "false" --type String --overwrite

and calling the GET /feature-flags endpoint again will return modified values:

{
  "FeatureA": false,
  "FeatureB": true,
  "FeatureC": false
}

That’s it!

How’s that useful?

Live feature flags transform application management by allowing changes without restarts or deployments. By injecting these flags with each request, updates happen in real-time, significantly speeding up the process and enhancing responsiveness.

However, this approach requires some app changes where the entire configuration is based on environment variables. You can no longer build config once at startup and use it as a singleton, so proper usage of dependency injection is vital.

As a reliable software company we’re focused on delivering the best quality IT services. However, we’ve discovered that programming skills give us a very particular opportunity...

Reach Us

65-392 Zielona Góra, Poland

Botaniczna 70

© 2015-2024 Codetain. All rights reserved.

cnlogo