In this guide, you'll use the targeting filter to roll out a feature to targeted audiences for your Node.js application. For more information about the targeting filter, see Roll out features to targeted audiences.
Prerequisites
- An Azure account with an active subscription. Create one for free.
- An App Configuration store, as shown in the tutorial for creating a store.
- A Beta feature flag with targeting filter. Create the feature flag.
- LTS versions of Node.js.
Create a web application with a feature flag
In this section, you create a web application that uses the Beta feature flag to control the access to the beta version of a web page.
Set up a Node.js Express project
Create a folder called
targeting-filter-tutorial
and initialize the project.mkdir targeting-filter-tutorial cd targeting-filter-tutorial npm init -y
Install the following packages.
npm install @azure/app-configuration-provider npm install @microsoft/feature-management npm install express
Create a new file named app.js and add the following code.
const express = require("express"); const server = express(); const port = "8080"; server.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); });
Connect to Azure App Configuration
Update the app.js and add the following code.
// Existing code ... const appConfigEndpoint = process.env.AZURE_APPCONFIG_ENDPOINT; const { DefaultAzureCredential } = require("@azure/identity"); const { load } = require("@azure/app-configuration-provider"); const { FeatureManager, ConfigurationMapFeatureFlagProvider } = require("@microsoft/feature-management"); let appConfig; let featureManager; async function initializeConfig() { // Load feature flags from App Configuration. appConfig = await load(appConfigEndpoint, new DefaultAzureCredential(), { featureFlagOptions: { enabled: true, refresh: { enabled: true } } }); // Create feature manager with feature flag provider that accesses feature flags from App Configuration. featureManager = new FeatureManager( new ConfigurationMapFeatureFlagProvider(appConfig)); } // Use a middleware to refresh the configuration before each request. server.use((req, res, next) => { appConfig.refresh(); next(); }); // Existing code ...
You connect to Azure App Configuration to load feature flags, enable automatic refresh, and create a
FeatureManager
object for accessing feature flags later. A middleware is added to refresh configuration before each request.Update the code to ensure the Express server starts only after the configuration has been successfully initialized.
// Existing code ... initializeConfig() .then(() => { // Start the express server. server.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); }); })
Use the feature flag
Add the following code to the app.js file to configure the route handler for the Express server. The server will serve different contents based on whether the Beta feature flag is enabled.
// Existing code ...
server.get("/", async (req, res) => {
const isBetaEnabled = await featureManager.isEnabled("Beta");
const [title, message] = isBetaEnabled
? ["Beta Page", "This is a beta page."]
: ["Home Page", "Welcome."];
res.send(
`<!DOCTYPE html>
<html>
<head><title>${title}</title></head>
<body style="display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0;">
<h1 style="text-align: center; font-size: 5rem;">${message}</h1>
</body>
</html>`
);
});
initializeConfig()
// Existing code ...
Enable targeting for the web application
A targeting context is required when evaluating features with targeting enabled. To explicitly provide this context for feature evaluation, you can pass it as a parameter to the featureManager.isEnabled
method.
const isBetaEnabled = await featureManager.isEnabled("Beta", { userId: "UserA", groups: ["Group1"] });
In a web application, the targeting context can also be provided as an ambient context by implementing the ITargetingContextAccessor interface. An ambient targeting context means that targeting information is automatically retrieved from the environment, such as the current HTTP request, without needing to explicitly pass it to each featureManager.isEnabled()
call.
You use ambient targeting context in this tutorial.
Add the following code after the Express server declaration. It uses
AsyncLocalStorage
to store the current request, allowing the feature manager to automatically retrieve the targeting context via a targeting context accessor callback. For more details, see Using AsyncLocalStorage for request context.const express = require("express"); const server = express(); const port = 8080; const { AsyncLocalStorage } = require("async_hooks"); const requestAccessor = new AsyncLocalStorage(); // Use a middleware to store request context. server.use((req, res, next) => { // Store the request in AsyncLocalStorage for this request chain. requestAccessor.run(req, () => { next(); }); }); // Create a targeting context accessor that retrieves user data from the current request. const targetingContextAccessor = { getTargetingContext: () => { // Get the current request from AsyncLocalStorage. const request = requestAccessor.getStore(); if (!request) { return undefined; } const { userId, groups } = request.query; return { userId: userId, groups: groups ? groups.split(",") : [] }; } }; // Existing code ...
When constructing the
FeatureManager
, pass the targeting context accessor to theFeatureManagerOptions
.featureManager = new FeatureManager( new ConfigurationMapFeatureFlagProvider(appConfig), { targetingContextAccessor: targetingContextAccessor });
After completing the previous steps, your app.js file should now contain the following complete implementation.
const express = require("express");
const server = express();
const port = 8080;
const { AsyncLocalStorage } = require("async_hooks");
const requestAccessor = new AsyncLocalStorage();
// Use a middleware to store request context
server.use((req, res, next) => {
// Store the request in AsyncLocalStorage for this request chain
requestAccessor.run(req, () => {
next();
});
});
// Create a targeting context accessor that retrieves user data from the current request
const targetingContextAccessor = {
getTargetingContext: () => {
// Get the current request from AsyncLocalStorage
const request = requestAccessor.getStore();
if (!request) {
return undefined;
}
const { userId, groups } = request.query;
return {
userId: userId,
groups: groups ? groups.split(",") : []
};
}
};
const appConfigEndpoint = process.env.AZURE_APPCONFIG_ENDPOINT;
const { DefaultAzureCredential } = require("@azure/identity");
const { load } = require("@azure/app-configuration-provider");
const { FeatureManager, ConfigurationMapFeatureFlagProvider } = require("@microsoft/feature-management");
let appConfig;
let featureManager;
async function initializeConfig() {
// Load feature flags from App Configuration.
appConfig = await load(appConfigEndpoint, new DefaultAzureCredential(), {
featureFlagOptions: {
enabled: true,
refresh: {
enabled: true
}
}
});
// Create feature manager with feature flag provider that accesses feature flags from App Configuration and targeting context accessor.
featureManager = new FeatureManager(
new ConfigurationMapFeatureFlagProvider(appConfig),
{
targetingContextAccessor: targetingContextAccessor
});
}
// Use a middleware to refresh the configuration before each request
server.use((req, res, next) => {
appConfig.refresh();
next();
});
server.get("/", async (req, res) => {
const isBetaEnabled = await featureManager.isEnabled("Beta");
const [title, message] = isBetaEnabled
? ["Beta Page", "This is a beta page."]
: ["Home Page", "Welcome."];
res.send(
`<!DOCTYPE html>
<html>
<head><title>${title}</title></head>
<body style="display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0;">
<h1 style="text-align: center; font-size: 5rem;">${message}</h1>
</body>
</html>`
);
});
// Initialize the configuration and start the server
initializeConfig()
.then(() => {
// Start the express server.
server.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
})
Targeting filter in action
Set the environment variable named AZURE_APPCONFIG_ENDPOINT to the endpoint of your App Configuration store found under the Overview of your store in the Azure portal.
If you use the Windows command prompt, run the following command and restart the command prompt to allow the change to take effect:
setx AZURE_APPCONFIG_ENDPOINT "<endpoint-of-your-app-configuration-store>"
If you use PowerShell, run the following command:
$Env:AZURE_APPCONFIG_ENDPOINT = "<endpoint-of-your-app-configuration-store>"
If you use macOS or Linux, run the following command:
export AZURE_APPCONFIG_ENDPOINT='<endpoint-of-your-app-configuration-store>'
Run the application.
node app.js
Open your browser and navigate to
localhost:8080
. You should see the default view of the app.-
- Add
userId
as a query parameter in the URL to specify the user ID. Visitlocalhost:8080/?userId=test@contoso.com
. You see the beta page, becausetest@contoso.com
is specified as a targeted user.
- Add
Visit
localhost:8080/?userId=testuser@contoso.com
. You cannot see the beta page, becausetestuser@contoso.com
is specified as an excluded user.
Next steps
To learn more about the feature filters, continue to the following documents.
For the full feature rundown of the JavaScript feature management library, continue to the following document.