Skip to content

Commit a7a7e75

Browse files
authored
Merge pull request #300986 from zhiyuanliang-ms/zhiyuanliang/js-variant-flag
Azure App Configuration - Add JavaScript variant feature flag doc
2 parents eedccf5 + ca7b9c8 commit a7a7e75

File tree

7 files changed

+268
-3
lines changed

7 files changed

+268
-3
lines changed

articles/azure-app-configuration/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@
214214
href: howto-variant-feature-flags-aspnet-core.md
215215
- name: Python
216216
href: howto-variant-feature-flags-python.md
217+
- name: JavaScript
218+
href: howto-variant-feature-flags-javascript.md
217219
- name: CI/CD integration
218220
items:
219221
- name: Use configuration files
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
title: 'Use variant feature flags in a Node.js application'
3+
titleSuffix: Azure App configuration
4+
description: In this tutorial, you learn how to use variant feature flags in a Node.js application
5+
#customerintent: As a user of Azure App Configuration, I want to learn how I can use variants and variant feature flags in my Node.js application.
6+
author: zhiyuanliang-ms
7+
ms.author: zhiyuanliang
8+
ms.service: azure-app-configuration
9+
ms.devlang: javascript
10+
ms.topic: how-to
11+
ms.date: 06/06/2025
12+
---
13+
14+
# Tutorial: Use variant feature flags in a Node.js application
15+
16+
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](./howto-variant-feature-flags.md). Before proceeding, ensure you create the variant feature flag named *Greeting* in your App Configuration store.
17+
18+
## Prerequisites
19+
20+
* [LTS versions of Node.js](https://github.com/nodejs/release#release-schedule).
21+
* Follow the [Use variant feature flags](./howto-variant-feature-flags.md) tutorial and create the variant feature flag named *Greeting*.
22+
23+
## Create a Node.js application
24+
25+
1. Create a folder called `quote-of-the-day` and initialize the project.
26+
27+
```bash
28+
mkdir quote-of-the-day
29+
cd quote-of-the-day
30+
npm init -y
31+
```
32+
33+
1. Install the following packages.
34+
35+
```bash
36+
npm install @azure/app-configuration-provider
37+
npm install @microsoft/feature-management
38+
npm install express
39+
```
40+
41+
1. Create a new file named *server.js* and add the following code.
42+
43+
```js
44+
const express = require("express");
45+
const server = express();
46+
47+
const appConfigEndpoint = process.env.AZURE_APPCONFIG_ENDPOINT;
48+
const { DefaultAzureCredential } = require("@azure/identity");
49+
const { load } = require("@azure/app-configuration-provider");
50+
const { FeatureManager, ConfigurationMapFeatureFlagProvider } = require("@microsoft/feature-management");
51+
52+
let appConfig;
53+
let featureManager;
54+
async function initializeConfig() {
55+
appConfig = await load(appConfigEndpoint, new DefaultAzureCredential(), {
56+
featureFlagOptions: {
57+
enabled: true,
58+
refresh: {
59+
enabled: true
60+
}
61+
}
62+
});
63+
64+
const featureFlagProvider = new ConfigurationMapFeatureFlagProvider(appConfig);
65+
featureManager = new FeatureManager(featureFlagProvider);
66+
}
67+
68+
function startServer() {
69+
// Use a middleware to refresh the configuration before each request
70+
server.use((req, res, next) => {
71+
appConfig.refresh();
72+
next();
73+
});
74+
server.use(express.json());
75+
// Serve static index.html from the current folder
76+
server.use(express.static("."));
77+
78+
// This API returns the different greeting messages based on the segment the user belongs to.
79+
// It evaluates a variant feature flag based on user context. The greeting message is retrieved from the variant configuration.
80+
server.get("/api/getGreetingMessage", async (req, res) => {
81+
const { userId, groups } = req.query;
82+
const variant = await featureManager.getVariant("Greeting", { userId: userId, groups: groups ? groups.split(",") : [] });
83+
res.status(200).send({ message: variant?.configuration });
84+
});
85+
86+
server.post("/api/like", (req, res) => {
87+
const { UserId } = req.body;
88+
if (UserId === undefined) {
89+
return res.status(400).send({ error: "UserId is required" });
90+
}
91+
// Here you would typically emit a 'like' event to compare variants.
92+
res.status(200).send();
93+
});
94+
95+
const port = "8080";
96+
server.listen(port, () => {
97+
console.log(`Server is running at http://localhost:${port}`);
98+
});
99+
}
100+
101+
// Initialize the configuration and start the server
102+
initializeConfig()
103+
.then(() => {
104+
startServer();
105+
})
106+
.catch((error) => {
107+
console.error("Failed to load configuration:", error);
108+
process.exit(1);
109+
});
110+
```
111+
112+
1. Create a new file named *index.html* and add the following code:
113+
114+
```html
115+
<!DOCTYPE html>
116+
<html lang="en">
117+
<head>
118+
<meta charset="UTF-8">
119+
<title>Quote of the Day</title>
120+
<style>
121+
.heart-button {
122+
background-color: transparent;
123+
border: none;
124+
cursor: pointer;
125+
font-size: 24px;
126+
}
127+
128+
.heart-button:hover {
129+
background-color: #F0F0F0;
130+
}
131+
</style>
132+
</head>
133+
<body>
134+
<div style="display: flex; flex-direction: column; min-height: 100vh; background-color: #f4f4f4;">
135+
<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;">
136+
<div style="font-size: 1.25em; color: black;">QuoteOfTheDay</div>
137+
</header>
138+
139+
<main style="display: flex; justify-content: center; align-items: center; flex-grow: 1;">
140+
<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;">
141+
<div id="quote-content">
142+
<h2 id="greeting">Quote of the Day</h2>
143+
<blockquote style="font-size: 2em; font-style: italic; color: #4EC2F7; margin: 0 0 20px 0; line-height: 1.4;">
144+
<p>"You cannot change what you are, only what you do."</p>
145+
<footer style="font-size: 0.55em; color: black; font-family: 'Arial', sans-serif; font-style: normal; font-weight: bold;">— Philip Pullman</footer>
146+
</blockquote>
147+
<div style="position: absolute; top: 10px; right: 10px; display: flex;">
148+
<button class="heart-button" id="like-button">
149+
<span id="heart-icon" style="color: #ccc">♥</span>
150+
</button>
151+
</div>
152+
</div>
153+
<div id="loading" style="display: none;">
154+
<p>Loading</p>
155+
</div>
156+
</div>
157+
</main>
158+
</div>
159+
160+
<script>
161+
// extract URL parameters to simulate user login
162+
document.addEventListener('DOMContentLoaded', function() {
163+
const urlParams = new URLSearchParams(window.location.search);
164+
const currentUser = urlParams.get('userId') || '';
165+
let liked = false;
166+
167+
const greetingElement = document.getElementById('greeting');
168+
const heartIcon = document.getElementById('heart-icon');
169+
const likeButton = document.getElementById('like-button');
170+
const quoteContent = document.getElementById('quote-content');
171+
const loadingElement = document.getElementById('loading');
172+
173+
async function init() {
174+
quoteContent.style.display = 'none';
175+
loadingElement.style.display = 'block';
176+
177+
const response = await fetch(`/api/getGreetingMessage?userId=${currentUser}`, {
178+
method: "GET"
179+
});
180+
const result = await response.json();
181+
greetingElement.textContent = result.message || "";
182+
quoteContent.style.display = 'block';
183+
loadingElement.style.display = 'none';
184+
}
185+
186+
likeButton.addEventListener('click', async function() {
187+
if (!liked) {
188+
const response = await fetch("/api/like", {
189+
method: "POST",
190+
headers: { "Content-Type": "application/json" },
191+
body: JSON.stringify({ UserId: currentUser }),
192+
});
193+
}
194+
liked = !liked;
195+
heartIcon.style.color = liked ? 'red' : '#ccc';
196+
});
197+
198+
init();
199+
});
200+
</script>
201+
</body>
202+
</html>
203+
```
204+
205+
For simplicity, the example extracts the `userId` from URL query parameters (e.g., `?userId=UserA`) to simulate different user identities.
206+
207+
## Run the application
208+
209+
1. 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.
210+
211+
If you use the Windows command prompt, run the following command and restart the command prompt to allow the change to take effect:
212+
213+
```cmd
214+
setx AZURE_APPCONFIG_ENDPOINT "<endpoint-of-your-app-configuration-store>"
215+
```
216+
217+
If you use PowerShell, run the following command:
218+
219+
```powershell
220+
$Env:AZURE_APPCONFIG_ENDPOINT = "<endpoint-of-your-app-configuration-store>"
221+
```
222+
223+
If you use macOS or Linux, run the following command:
224+
225+
```bash
226+
export AZURE_APPCONFIG_ENDPOINT='<endpoint-of-your-app-configuration-store>'
227+
```
228+
229+
1. Run the application.
230+
231+
```bash
232+
node server.js
233+
```
234+
235+
1. Open your browser and navigate to `localhost:8080`. You should see the default view of the app that doesn't have any greeting message.
236+
237+
:::image type="content" source="media/howto-variant-feature-flags-javascript/default-variant.png" alt-text="Screenshot of the Quote of the day app, showing no greeting message for the user.":::
238+
239+
1. You can use `userId` query parameter in the url to specify the user ID. Visit `localhost:8080/?userId=UserA` and you see a long greeting message.
240+
241+
:::image type="content" source="media/howto-variant-feature-flags-javascript/long-variant.png" alt-text="Screenshot of the Quote of the day app, showing long greeting message for the user.":::
242+
243+
1. 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.
244+
245+
:::image type="content" source="media/howto-variant-feature-flags-javascript/simple-variant.png" alt-text="Screenshot of the Quote of the day app, showing simple greeting message for the user.":::
246+
247+
## Next steps
248+
249+
For the full feature rundown of the JavaScript feature management library, refer to the following document.
250+
251+
> [!div class="nextstepaction"]
252+
> [JavaScript Feature Management](./feature-management-javascript-reference.md)

articles/azure-app-configuration/howto-variant-feature-flags.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ In this tutorial, you create a web app named _Quote of the Day_. When the app is
7272
| Simple | "Hello!" | 25% |
7373
| Long | "I hope this makes your day!" | 25% |
7474

75-
2. Continue to the following instructions to use the variant feature flag in your application for the language or platform you're using.
76-
* [ASP.NET Core](./howto-variant-feature-flags-aspnet-core.md)
77-
* [Python](./howto-variant-feature-flags-python.md)
75+
## Next steps
76+
77+
Continue to the following instructions to use the variant feature flag in your application for the language or platform you're using
78+
79+
> [!div class="nextstepaction"]
80+
> [ASP.NET Core](./howto-variant-feature-flags-aspnet-core.md)
81+
82+
> [!div class="nextstepaction"]
83+
> [Python](./howto-variant-feature-flags-python.md)
84+
85+
> [!div class="nextstepaction"]
86+
> [JavaScript](./howto-variant-feature-flags-javascript.md)

articles/azure-app-configuration/index.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ landingContent:
161161
url: howto-variant-feature-flags.md
162162
- text: Use variant feature flags in Python
163163
url: howto-variant-feature-flags-python.md
164+
- text: Use variant feature flags in JavaScript
165+
url: howto-variant-feature-flags-javascript.md
164166
- linkListType: reference
165167
links:
166168
- text: Overview
57.7 KB
Loading
68.5 KB
Loading
60.2 KB
Loading

0 commit comments

Comments
 (0)