Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In this tutorial, you use a variant feature flag to manage experiences for different user segments in an example application, Quote of the Day. You utilize the variant feature flag created in Use variant feature flags. Before proceeding, ensure you create the variant feature flag named Greeting in your App Configuration store.
Prerequisites
- LTS versions of Node.js.
- Follow the Use variant feature flags tutorial and create the variant feature flag named Greeting.
Create a Node.js application
Create a folder called
quote-of-the-day
and initialize the project.mkdir quote-of-the-day cd quote-of-the-day 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 server.js and add the following code.
const express = require("express"); const server = express(); 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() { appConfig = await load(appConfigEndpoint, new DefaultAzureCredential(), { featureFlagOptions: { enabled: true, refresh: { enabled: true } } }); const featureFlagProvider = new ConfigurationMapFeatureFlagProvider(appConfig); featureManager = new FeatureManager(featureFlagProvider); } function startServer() { // Use a middleware to refresh the configuration before each request server.use((req, res, next) => { appConfig.refresh(); next(); }); server.use(express.json()); // Serve static index.html from the current folder server.use(express.static(".")); // This API returns the different greeting messages based on the segment the user belongs to. // It evaluates a variant feature flag based on user context. The greeting message is retrieved from the variant configuration. server.get("/api/getGreetingMessage", async (req, res) => { const { userId, groups } = req.query; const variant = await featureManager.getVariant("Greeting", { userId: userId, groups: groups ? groups.split(",") : [] }); res.status(200).send({ message: variant?.configuration }); }); server.post("/api/like", (req, res) => { const { UserId } = req.body; if (UserId === undefined) { return res.status(400).send({ error: "UserId is required" }); } // Here you would typically emit a 'like' event to compare variants. res.status(200).send(); }); const port = "8080"; server.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); }); } // Initialize the configuration and start the server initializeConfig() .then(() => { startServer(); }) .catch((error) => { console.error("Failed to load configuration:", error); process.exit(1); });
Create a new file named index.html and add the following code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Quote of the Day</title> <style> .heart-button { background-color: transparent; border: none; cursor: pointer; font-size: 24px; } .heart-button:hover { background-color: #F0F0F0; } </style> </head> <body> <div style="display: flex; flex-direction: column; min-height: 100vh; background-color: #f4f4f4;"> <header style="background-color: white; border-bottom: 1px solid #eaeaea; display: flex; justify-content: space-between; align-items: center; font-family: 'Arial', sans-serif; font-size: 16px;"> <div style="font-size: 1.25em; color: black;">QuoteOfTheDay</div> </header> <main style="display: flex; justify-content: center; align-items: center; flex-grow: 1;"> <div style="background-color: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); max-width: 700px; position: relative; text-align: left;"> <div id="quote-content"> <h2 id="greeting">Quote of the Day</h2> <blockquote style="font-size: 2em; font-style: italic; color: #4EC2F7; margin: 0 0 20px 0; line-height: 1.4;"> <p>"You cannot change what you are, only what you do."</p> <footer style="font-size: 0.55em; color: black; font-family: 'Arial', sans-serif; font-style: normal; font-weight: bold;">— Philip Pullman</footer> </blockquote> <div style="position: absolute; top: 10px; right: 10px; display: flex;"> <button class="heart-button" id="like-button"> <span id="heart-icon" style="color: #ccc">♥</span> </button> </div> </div> <div id="loading" style="display: none;"> <p>Loading</p> </div> </div> </main> </div> <script> // extract URL parameters to simulate user login document.addEventListener('DOMContentLoaded', function() { const urlParams = new URLSearchParams(window.location.search); const currentUser = urlParams.get('userId') || ''; let liked = false; const greetingElement = document.getElementById('greeting'); const heartIcon = document.getElementById('heart-icon'); const likeButton = document.getElementById('like-button'); const quoteContent = document.getElementById('quote-content'); const loadingElement = document.getElementById('loading'); async function init() { quoteContent.style.display = 'none'; loadingElement.style.display = 'block'; const response = await fetch(`/api/getGreetingMessage?userId=${currentUser}`, { method: "GET" }); const result = await response.json(); greetingElement.textContent = result.message || ""; quoteContent.style.display = 'block'; loadingElement.style.display = 'none'; } likeButton.addEventListener('click', async function() { if (!liked) { const response = await fetch("/api/like", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ UserId: currentUser }), }); } liked = !liked; heartIcon.style.color = liked ? 'red' : '#ccc'; }); init(); }); </script> </body> </html>
For simplicity, the example extracts the
userId
from URL query parameters (e.g.,?userId=UserA
) to simulate different user identities.
Run the application
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 server.js
Open your browser and navigate to
localhost:8080
. You should see the default view of the app that doesn't have any greeting message.You can use
userId
query parameter in the url to specify the user ID. Visitlocalhost:8080/?userId=UserA
and you see a long greeting message.Try different user IDs to see how the variant feature flag changes the greeting message for different segments of users. Visit
localhost:8080/?userId=UserB
and you see a shorter greeting message.
Next steps
For the full feature rundown of the JavaScript feature management library, refer to the following document.