Feature flags with Astro + Vercel Toolbar
I’ve been using Vercel for a few years now to host my website(s), but I never really looked into the Vercel Toolbar until now. I though it was mainly used for leaving comments, but apparently it has evolved a lot, and Vercel added a lot of new capabilities. One of those caught my eye: the feature flags. Feature flags is a concept where you can toggle on or off certain functionalities, allowing for controlled release and testing of new features.
Now we know what feature flags are, let’s see how to implement them in an Astro website.
Adding the Vercel Toolbar locally
We start by adding the @vercel/toolbar
& @vercel/flags
dependencies.
The use the vercel CLI
to link your local project to your Vercel project
Vercel injects the Toolbar into your deployment previews automatically, but while developing locally of course this is not available.
That’s why I added the check on the NODE_ENV variable, to only add the script when we’re developing locally.
So let’s add the Toolbar to the Layout
, which is shared by all pages on my website.
We just need to add a <script>
tag to our Astro code.
To know what to fill in on the data-owner-id
and data-project-id
, it’s best to follow Vercels guide.
Once this is set up, you should see the toolbar appear at the bottom of the page:
Setting up Feature Flags
First we need to set an environment variable called FLAGS_SECRET
inside the Vercel settings for our website, and then pull the environment variable using vercel env pull
.
The FLAGS_SECRET value must have a specific length (32 random bytes encoded in base64) to work as an encryption key.
You can generate this value using nodejs
:
The Vercel Toolbar will send GET requests to an endpoint with the URL .well-known/vercel/flags
on the current domain to retrieve the configuration for the feature flags.
So we should make sure we have an API route which listens on this URL for GET requests, and returns a configuration.
The configuration should have the following shape:
Let’s implement this into an API route in our Astro codebase:
Note that I added export const prerender = false
at the top of the file, because I’m using hybrid rendering, it’s necessary to flag non-static routes.
You can also see I pass a second argument to the verifyAccess
(and the decrypt
later) function, which is the FLAGS_SECRET
environment variable we set earlier coming from import.meta.env
, since Vite-based frameworks like Astro don’t use process.env
, and therefore the FLAGS_SECRET is not read from the environment by the @vercel/flags package automatically.
The verifyAccess
token will make sure the request to our API route is actually coming from the Vercel Toolbar.
When we click on the feature flags button inside of the toolbar we’ll see our newly created feature flag pop up:
Once we select one of the overrides, a cookie with the key vercel-flag-overrides
will be created, containing the encypted value of the flag.
You’ll also see the Vercel Toolbar turns purple when you activate a flag.
Now we want to actually read the flag and do something with it. I decided to use the flag as an indication that the homepage should be redirected to another version of the homepage. Because I want to redirect as early as possible, I decided to put this logic into a middleware. In the middleware, we can read the cookies of the incoming request, and execute logic based on that.
So we get the cookies from the context
, check if the requested page is the homepage and read the vercel-flag-overrides
cookie’s value.
If there’s a cookie and the request is for the homepage, we decrypt
the cookie’s value and check if the newFeature
flag is set to true
.
If this is the case, we’ll redirect the user (using context.redirect()
) to an alternative homepage.
On more thing that needed to be changed was that I had to change my homepage to also be server rendered (so adding export const prerender = false;
to the top of the file) instead of statically rendered, otherwise it was not possible to read the cookies for the request for that page.
The source code can be found on Github.
Thanks for reading! Hope it helps someone further.