@flagify/astro
v1.5.0
Published
Astro integration for Flagify — feature flags with dev toolbar and middleware.
Maintainers
Readme
Overview
@flagify/astro is the official Astro integration for Flagify. Evaluate feature flags in Astro pages and components with a dev toolbar for overrides and a Vercel Flags SDK adapter.
- Astro-native -- Integration with auto-injected middleware and dev toolbar
defineFlagAPI -- Declare flags with typed defaults and options- Dev toolbar -- Toggle flag overrides during development via cookie persistence
- Vercel Flags SDK -- Built-in adapter via
@flagify/astro/adapter - SSR + SSG -- Works in both server-rendered and static builds
- Lightweight -- Thin wrapper over
@flagify/node
Table of contents
- Installation
- Quick start
- Define flags
- Evaluate in pages
- Flag evaluation priority
- Dev toolbar
- Vercel Flags SDK adapter
- Client management
- SSG limitations
- TypeScript
- API reference
- Contributing
- License
Installation
# pnpm
pnpm add @flagify/astro @flagify/node
# npm
npm install @flagify/astro @flagify/node
# yarn
yarn add @flagify/astro @flagify/nodePeer dependency: Astro >= 4.0.0
Quick start
1. Add environment variables
# .env
FLAGIFY_PROJECT_KEY=your-project-key
FLAGIFY_PUBLIC_KEY=pk_dev_xxx
FLAGIFY_SECRET_KEY=sk_dev_xxx # optional, for SSR evaluation2. Add the integration
// astro.config.mjs
import { defineConfig } from 'astro/config';
import flagify from '@flagify/astro';
export default defineConfig({
integrations: [flagify()],
});This automatically registers:
- Middleware that initializes the Flagify client and parses override cookies
- Dev toolbar app for toggling flag overrides during development
3. Define and evaluate flags
// src/flags.ts
import { defineFlag } from '@flagify/astro';
export const newCheckout = defineFlag({
key: 'new-checkout-flow',
description: 'New checkout flow experience',
default: false,
});---
// src/pages/index.astro
import { newCheckout } from '../flags';
const isNewCheckout = await newCheckout(Astro);
---
{isNewCheckout && <NewCheckoutBanner />}Define flags
Use defineFlag to declare feature flags with typed defaults and optional variant options.
import { defineFlag } from '@flagify/astro';
// Boolean flag
export const darkMode = defineFlag({
key: 'dark-mode',
description: 'Enable dark mode',
default: false,
});
// String variant with options (used by the dev toolbar)
export const heroVariant = defineFlag({
key: 'hero-variant',
description: 'A/B test for hero section',
default: 'control',
options: [
{ value: 'control', label: 'Control' },
{ value: 'variant-a', label: 'Variant A' },
],
});
// Number flag
export const maxItems = defineFlag({
key: 'max-items',
description: 'Maximum items per page',
default: 20,
});Evaluate in pages
Each flag defined with defineFlag returns an async function that accepts the Astro global.
---
import { newCheckout, heroVariant, maxItems } from '../flags';
const isNewCheckout = await newCheckout(Astro);
const hero = await heroVariant(Astro);
const max = await maxItems(Astro);
---
{isNewCheckout && <NewCheckoutBanner />}
<Hero variant={hero} />
<ItemList max={max} />Flag evaluation priority
- Override cookie -- dev overrides from the toolbar take highest priority
- Flagify SDK -- evaluated via
@flagify/node(cached locally) - Default value -- the
defaultfromdefineFlag()
Dev toolbar
In development mode, the Flagify toolbar app lets you set flag overrides as JSON. Overrides are stored in a flagify-overrides cookie and persist across page navigations.
Vercel Flags SDK adapter
For projects using the Vercel Flags SDK:
import { createFlagifyAdapter } from '@flagify/astro/adapter';
const { adapter } = createFlagifyAdapter();
export const checkout = flag({
key: 'new-checkout-flow',
adapter: adapter('new-checkout-flow'),
defaultValue: false,
});The adapter supports user targeting when entities are passed:
const { adapter } = createFlagifyAdapter({ origin: 'https://app.flagify.dev' });
export const premium = flag({
key: 'premium-feature',
adapter: adapter('premium-feature'),
defaultValue: false,
});Client management
The integration manages a singleton @flagify/node client. For advanced use cases, you can control it directly:
import { initClient, getClient, waitForClient, destroyClient } from '@flagify/astro';
// Initialize (no-op if already initialized)
initClient({ projectKey: 'proj_xxx', publicKey: 'pk_xxx' });
// Wait for initial flag sync
await waitForClient();
// Access the client
const client = getClient();
client?.isEnabled('my-flag');
// Cleanup
destroyClient();SSG limitations
In static builds (SSG), flags are evaluated at build time with no per-visitor user context. Since @flagify/astro v1.1.0, catch-all and rollout targeting rules are applied at build time against the anonymous context, so kill switches, global enables, and percentage rollouts are baked in correctly. Rules that target by user attributes (segments, conditions on role, plan, email, custom attrs, etc.) can't be evaluated at build time and will miss — for those, use SSR mode and pass the request's user to flagify.evaluate(key, user) per request.
TypeScript
Add type safety for context.locals:
// src/env.d.ts
/// <reference types="astro/client" />
declare namespace App {
interface Locals {
flagifyOverrides: Record<string, unknown>;
}
}API reference
Main entrypoint (@flagify/astro)
| Export | Type | Description |
|--------|------|-------------|
| default (flagify) | Function | Astro integration -- registers middleware and dev toolbar |
| defineFlag | Function | Define a feature flag with typed defaults |
| initClient | Function | Initialize the singleton Flagify client |
| getClient | Function | Get the current client instance (or null) |
| waitForClient | Function | Wait for initial flag sync to complete |
| destroyClient | Function | Destroy the client and free resources |
| FlagifyAstroOptions | Type | Integration options (extends FlagifyOptions) |
| FlagDefinition | Type | Shape of a flag definition |
| FlagifyLocals | Type | Shape of context.locals set by middleware |
| FlagEvaluator | Type | Return type of defineFlag() |
Middleware (@flagify/astro/middleware)
| Export | Type | Description |
|--------|------|-------------|
| onRequest | Function | Astro middleware -- inits client and parses override cookies |
Adapter (@flagify/astro/adapter)
| Export | Type | Description |
|--------|------|-------------|
| createFlagifyAdapter | Function | Creates a Vercel Flags SDK compatible adapter |
Verifying webhook signatures
@flagify/astro re-exports the verification helpers from @flagify/node (verifyWebhookSignature, constructWebhookEvent, WebhookSignatureError, plus the WebhookEvent types) and ships defineWebhookHandler — a factory that returns an Astro APIRoute for your webhook endpoint.
// src/pages/api/flagify-webhook.ts
import { defineWebhookHandler } from "@flagify/astro";
export const POST = defineWebhookHandler({
secret: import.meta.env.FLAGIFY_WEBHOOK_SECRET,
onEvent: async (event) => {
// Idempotency: persist event.id and skip duplicates on retry.
console.log(event.event, event.data.environmentId);
},
});The handler returns:
200onceonEventresolves.403when the signature does not validate (with the failure code in the body).500ifonEventthrows.
Astro must be running in SSR mode (or a hybrid route) — static pages cannot serve webhooks. See verifyWebhookSignature / constructWebhookEvent in @flagify/node for the lower-level API and the optional tolerance / now options.
Debug logging (FLAGIFY_DEBUG)
@flagify/astro builds on @flagify/node, so the underlying client is silent in normal operation. To diagnose realtime/SSE issues during development, opt in with the FLAGIFY_DEBUG env var:
FLAGIFY_DEBUG=1 astro devWhen enabled, the client emits verbose console.info / console.debug lines for SSE connect, reconnect, initial sync, flag changes, and idle-timeout warnings (server heartbeat every 15s; client watchdog defaults to 45s of silence before forcing a reconnect). Real errors (failed evaluation, missing client init in middleware) always surface regardless. SSG builds run a fresh client per build and never open a long-lived stream — FLAGIFY_DEBUG mostly matters for astro dev and SSR deployments.
Contributing
We welcome contributions. Please open an issue first to discuss what you'd like to change.
# Clone
git clone https://github.com/flagifyhq/javascript.git
cd javascript
# Install
pnpm install
# Development (watch mode)
pnpm run dev
# Build
pnpm run buildLicense
MIT -- see LICENSE for details.
