“I love tailing the logs and refreshing my release dashboard!” said no developer ever.
Releases are a prime area where bad ships can happen. Progressive rollouts allow you to release features to your users, a little bit at a time, at a pace you control. Good news - progressive rollouts are now generally available in LaunchDarkly’s free plan!
In this tutorial, you will learn how to use progressive rollouts in your ExpressJS application to upgrade to a new API version. Three raccoons in a trench coat – I mean, our team of backend developers – have been hard at work coding a new version of the raccoon facts API. We’d hate to break our beloved Raccoon Facts website, so let’s roll out the new API progressively so we can monitor for any regressions that might come up.
Our stack: Express.js, Pug, and Render
Express is the most popular, mature, stable Node.js server framework. As of publication, it has over 65k stars on GitHub, 33 million weekly downloads, and is hosted by the OpenJS Foundation.
The example API used in this project was deployed to a free Render account in under 5 minutes. Crowdsourcing the facts from X/Twitter and Mastodon took longer than writing the code and getting it into production.
We’ll be using the Pug templating engine. This approach keeps the LaunchDarkly logic and API calls contained in the server layer. Pug has 21.7k stars on GitHub, over a million weekly downloads, a built-in Express integration, and a totally adorable name. It offers a clean, whitespace-sensitive templating syntax similar to Ruby’s haml.
Do pugs even get along with raccoons? I guess we’re about to find out.
Prerequisites
- A LaunchDarkly account - sign up for free here
- A local developer environment with git, Node.js, and npm installed
Get started with the example Express LaunchDarkly app
We’ve written an example app that shows you a raccoon fact. It has a routing layer and a minimalist front end, since the raccoons couldn’t afford any designers.
We’ll add logic to the routing layer to choose which version of the API to use based on a LaunchDarkly flag value. Then, we’ll use the progressive rollout workflow to gradually switch to the new version of the API.
Clone the example repository, install dependencies, and start the server by running the following commands in your terminal:
git clone https://github.com/annthurium/expressjs-raccoon-facts
cd expressjs-raccoon-facts
npm install
node index.js
If you open http://localhost:3000/ in a browser, you should see a page with a raccoon fact and the API version. 🦝
Gating API updates with a LaunchDarkly flag in an ExpressJS application
Log in to your LaunchDarkly account. Copy your SDK key from the environment dropdown menu:
Paste it into the .env.example file in the repository. Rename the file to “.env.” and save.
In your editor, open index.js. Modify the code as commented below.
const express = require("express");
// add these 2 new dependencies
const ld = require("@launchdarkly/node-server-sdk");
require("dotenv").config();
const app = express();
app.set("view engine", "pug");
// add the following 2 lines to initialize the LaunchDarkly SDK
const sdkKey = process.env.LAUNCHDARKLY_SDK_KEY;
const ldClient = ld.init(sdkKey);
const getRaccoonFact = async function (apiVersion) {
const url = `https://versioned-api.onrender.com/v${apiVersion}/fact/`;
const response = await fetch(url);
const data = await response.json();
return data;
};
app.get("/", async function (req, res) {
// Add the following variable assignment to set the user context that will be sent to LaunchDarkly
const context = {
kind: "user",
key: "user-key-123abcde",
email: "raccacoonie@everything.pizza",
};
// replace the previous apiVersion variable assignment with these lines of code
// that evaluates the flag value so we know what version of the API to use
const apiVersion = await ldClient.variation(
"raccoon_facts_api",
context,
0
);
const raccoonFact = await getRaccoonFact(apiVersion);
res.render("index.pug", { raccoonFact, apiVersion });
});
const port = 3000;
const server = app.listen(port, function (err) {
if (err) console.log("Error in server setup");
console.log(`Server listening on http://localhost:${port}`);
});
// Add the following new function to gracefully close the connection to the LaunchDarkly server.
process.on("SIGTERM", () => {
debug("SIGTERM signal received: closing HTTP server");
ld.close();
server.close(() => {
debug("HTTP server closed");
ldClient.close();
});
});
Save index.js and restart your server. If you reload http://localhost:3000/ in your browser, you should still see a fact from Version 0 of the API.
Set up your progressive rollout
Head over to the LaunchDarkly app.
Create a flag by clicking either of the “Create flag” buttons.
Configure the flag as follows:
- Name: raccoon_facts_api
- Description: Upgrade the raccoon facts API from V0 to V1.
- Configuration: release
- Flag type: Number
- Variations:
- API version 0: 0
- API version 1: 1
- When targeting is OFF, serve version 0
- When targeting is ON, serve version 1
Click “Create flag.”
Adding a progressive rollout to a LaunchDarkly flag
On the next screen, click the “Add rollout” button next to “Default rule” and select “Progressive rollout.”
The progressive rollout dialog has some sensible defaults. If I was rolling out a production API, I’d probably keep these as is.
The backend raccoons ain’t got time for that, and I bet you don’t either. I’ll remove some steps so you can see what a completed rollout looks like in just two minutes.
Delete unwanted rollout stages by clicking on the “...” button, and selecting “Delete.”
Your finished configuration should be the following:
- Turn flag on: On
- Variation to roll out: v1
- Context kind to roll out by: user
- Roll out to 25% for 1 minute
- Roll out to 50% for 1 minute
When everything looks good to go, click the “Confirm” button.
On the next screen click “Review and save.”
If your settings require confirming changes, add a comment here: “Rolling out v1 of raccoon facts api. Trash bandits, assemble!” Type the name of the environment to confirm. Click “Save changes.”
The next screen will show the progress of your rollout:
Take a two minute break. Go get a coffee, or rummage through some trash. I’m not here to judge.
After that time has passed, reload http://localhost:3000/.. You should see new, wackier facts from the v1 version of the API.
The Progressive Rollout UI will also update to indicate that version 1 is being served to 100% of users. 🎉
In this post you’ve learned to use LaunchDarkly’s progressive rollouts to migrate to a newer version of an API, automatically, at a pace that’s most comfortable to you. Progressive rollouts can be used for your regularly scheduled releases as well as riskier launches.
If you want to show off what you’ve built, or just share your most outlandish raccoon fact, holler at me via email (tthurium@launchdarkly.com), X/Twitter, Mastodon, Discord or LinkedIn.