Flagship + AWS Lambda Integration
This project demonstrates how to integrate Flagship feature flags with AWS Lambda, enabling feature flagging and A/B testing in your serverless applications.
Github Repository
Overview
This guide shows how to:
- Initialize the Flagship SDK in an AWS Lambda function
- Configure the SDK for optimal performance in serverless environments
- Create a visitor object with context data from API Gateway events
- Fetch feature flags assigned to this visitor
- Retrieve specific flag values for use in the application
- Send analytics data back to Flagship
- Ensure analytics are sent before the Lambda function terminates
Prerequisites
- Node.js (v22 or later)
- AWS SAM CLI
- Docker (for local testing)
- An AWS account
- A Flagship account with API credentials
Setup
- Clone the example repository:
git clone https://github.com/flagship-io/flagship-aws-lambda-example.git
cd flagship-aws-lambda-example
- Install dependencies:
cd hello-world
npm install
- Configure your Flagship credentials in the template.yaml file:
Environment:
Variables:
FLAGSHIP_ENV_ID: "your-env-id"
FLAGSHIP_API_KEY: "your-api-key"
- Build and deploy your application:
sam build
sam deploy --guided
Initialize the Flagship SDK in an AWS Lambda Function
When working with AWS Lambda, it's important to initialize the Flagship SDK outside the handler function to leverage container reuse, which improves performance for subsequent invocations.
import {
DecisionMode,
Flagship,
LogLevel,
} from "@flagship.io/js-sdk";
/**
* Access Flagship credentials from environment variables
*/
const { FLAGSHIP_ENV_ID, FLAGSHIP_API_KEY } = process.env;
/**
* Initialize Flagship SDK with credentials and configuration.
* IMPORTANT: We initialize outside the handler function to leverage Lambda container reuse.
*/
await Flagship.start(FLAGSHIP_ENV_ID, FLAGSHIP_API_KEY, {
logLevel: LogLevel.DEBUG,
// Decision mode can be BUCKETING or DECISION_API
decisionMode: DecisionMode.BUCKETING,
// Defer fetching campaign data until explicitly needed
fetchNow: false,
// Disable polling for campaigns since Lambda functions are ephemeral
pollingInterval: 0,
trackingManagerConfig: {
// Setting batchIntervals to 0 prevents background timers for analytics
batchIntervals: 0,
},
// Improve performance by sending multiple activate flags in a single request
batchActivateHits: true,
});
/**
* AWS Lambda handler function
*/
export const lambdaHandler = async (event, context) => {
// Your Lambda function logic here
};
Configuration Options
-
decisionMode:
BUCKETING
makes decisions locally using pre-fetched campaigns (recommended for Lambda)DECISION_API
calls Flagship servers for each decision (more network overhead)
-
fetchNow:
false
defers fetching campaign data until explicitly needed, improving cold start times
-
pollingInterval:
0
disables automatic polling since Lambda functions are ephemeral
-
trackingManagerConfig.batchIntervals:
0
prevents Flagship from using background timers for analytics, which is critical because Lambda may terminate after function completion
-
batchActivateHits:
true
improves performance by sending multiple activate flags in a single request
Create a visitor object with context from API Gateway events
The visitor object represents a user of your application. In AWS Lambda, you can extract context data from the API Gateway event:
// From the Lambda handler function
const visitorId = event.queryStringParameters?.visitorId;
/**
* Create a Flagship visitor with contextual data from request headers
*/
const visitor = Flagship.newVisitor({
visitorId,
// Set GDPR consent status for data collection
hasConsented: true,
context: {
userAgent: event.headers["user-agent"] || "unknown",
country: event.headers["cloudfront-viewer-country"] || "unknown",
path: event.path || "unknown",
referrer: event.headers["referer"] || "unknown",
// You can add any additional context that's relevant for targeting
// isPremiumUser: event.queryStringParameters?.premium === "true",
// deviceType: detectDeviceType(event.headers["user-agent"]),
},
});
You can include any information in the context object that might be useful for targeting. API Gateway events provide access to HTTP headers, query parameters, and path information. Common examples include:
- Demographics: location, language preferences
- Technical: device type, browser, operating system
- Behavioral: account type, subscription status
- Custom: any application-specific attributes
Fetch feature flags assigned to this visitor
Once you have a visitor object, you need to fetch the feature flags assigned to them based on targeting rules:
// Fetch all feature flags assigned to this visitor based on targeting rules
await visitor.fetchFlags();
// Continue with Lambda function logic
This operation evaluates all campaign rules against the visitor's context and assigns flag variations accordingly.
Retrieve specific flag values for use in the application
After fetching flags, you can retrieve specific flag values to control your application's behavior:
// Retrieve specific flag values with default fallbacks
const welcomeMessage = visitor
.getFlag("welcome_message")
.getValue("Welcome to our site!");
const isFeatureEnabled = visitor
.getFlag("new_feature_enabled")
.getValue(false);
// You can get different types of values:
// Numbers
const discountPercentage = visitor
.getFlag("discount_percentage")
.getValue(0);
// Objects
const uiConfig = visitor
.getFlag("ui_config")
.getValue({
theme: "light",
showBanner: false,
});
// Arrays
const enabledFeatures = visitor
.getFlag("enabled_features")
.getValue(["basic"]);
Always provide a default value that matches the expected type. This ensures your application works even if the flag isn't defined or there's an issue fetching flags.
Note: Calling
getValue
automatically activates the flag, meaning it will be counted in reporting.
Send analytics data back to Flagship
To measure the impact of your feature flags, send analytics data back to Flagship:
/**
* Send analytics events back to Flagship for campaign reporting and optimization.
*/
visitor.sendHits([
{
type: HitType.PAGE_VIEW,
documentLocation: event.requestContext?.http?.path || event.path || "unknown",
},
{
type: HitType.EVENT,
category: EventCategory.ACTION_TRACKING,
action: "feature_view",
label: "new_feature",
value: isFeatureEnabled ? 1 : 0,
},
]);
Analytics data is crucial for measuring the impact of your feature flags. You can track page views, events, transactions, and more to understand how different flag variations affect user behavior.
Ensure analytics are sent before the Lambda function terminates
AWS Lambda functions can terminate immediately after completion, potentially losing unsent analytics data. To prevent this, call Flagship.close()
before returning:
// Ensure all analytics data is sent before the Lambda function terminates
await Flagship.close();
// Return the response
const response = {
statusCode: 200,
body: JSON.stringify({
message: welcomeMessage,
features: {
newFeatureEnabled: isFeatureEnabled,
},
}),
};
return response;
This ensures that all pending analytics are sent before the Lambda function terminates, giving you accurate reporting data.
Production Considerations
When deploying this integration in production, consider the following best practices:
Security
- Environment Variables: Store sensitive information like API keys or ENV ID in environment variables or AWS Secrets Manager.
Cold Starts and Warm Containers
AWS Lambda has the concept of "cold starts" and "warm containers":
- Cold start: First invocation after deployment or when scaling up
- Warm container: Subsequent invocations reusing the same container
To optimize performance:
- Initialize the Flagship SDK outside the handler function
- Use the Bucketing decision mode to minimize network calls
- Set
fetchNow: false
to defer campaign fetching until needed - Consider using Provisioned Concurrency for critical Lambda functions
Learn More
Updated about 13 hours ago