Featurevisor

Framework guides

Astro

Evaluate in Astro pages as the server renders them, so the browser receives HTML and never the datafile.

Create an Astro project

Command
npm create astro@latest

Install the SDK

Command
npm install @featurevisor/sdk

The JavaScript SDK page is the reference for every method.

Share one instance on the server

This example targets Astro's Node.js adapter. Install it with npx astro add node and enable on demand rendering for personalized pages. Prerendered pages evaluate once at build time, without a visitor's request context, so a flag change reaches them only when the site is built again.

Save the shared ES module refresh helper unchanged as src/datafile-refresh.mjs. Use only the helper, not the Node.js startup timer.

Create a module for the shared instance. Set FEATUREVISOR_DATAFILE_URL in the Node server's environment:

src/featurevisor.mjs
import { createFeaturevisor } from '@featurevisor/sdk'
import { refreshDatafile } from './datafile-refresh.mjs'
const instance = createFeaturevisor({})
let ready = false
let nextRefreshAt = 0
export async function getInstance() {
const datafileUrl = process.env.FEATUREVISOR_DATAFILE_URL
if (!datafileUrl) throw new Error('Set FEATUREVISOR_DATAFILE_URL')
if (Date.now() >= nextRefreshAt) {
try {
await refreshDatafile(instance, datafileUrl)
ready = true
nextRefreshAt = Date.now() + 5 * 60 * 1000
} catch (error) {
if (!ready) throw error
nextRefreshAt = Date.now() + 30 * 1000
console.error('Configuration refresh failed:', error)
}
}
return instance
}

Import only from server code. Each worker waits for its first valid datafile, then refreshes on requests after five minutes. Failures retain the previous datafile and retry after thirty seconds. Keep one fixed URL per instance.

Requests drive the refresh, so no timer is needed.

Evaluate in a page

Every visitor needs a stable ID to be bucketed by. This page gives visitors who are not signed in a random ID in a cookie on their first visit, and uses it as userId:

src/pages/index.astro
---
import { getInstance } from '../featurevisor.mjs'
export const prerender = false
Astro.response.headers.set('Cache-Control', 'private, no-store')
let userId = Astro.cookies.get('visitorId')?.value
if (!userId) {
userId = crypto.randomUUID()
Astro.cookies.set('visitorId', userId, {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: import.meta.env.PROD,
maxAge: 60 * 60 * 24 * 365,
})
}
const f = await getInstance()
const context = {
userId,
}
const newCheckout = f.isEnabled('checkout', context)
---
{newCheckout ? <NewCheckout /> : <Checkout />}
  • The visitor can change the cookie, so it is good for bucketing and nothing else. For signed in users, use the ID your authentication middleware verified, from Astro.locals.
  • Use the same ID wherever the visitor is evaluated, on the server and in the browser. Never fall back to one ID shared by every visitor, which would put them all in one bucket.
  • Pass context to each evaluation. Never call setContext() on the shared instance.
  • Keep personalized HTML out of shared caches, as the Cache-Control header does here.

The Astro cookie API lists the cookie options.

Previous
Nuxt