@occollective/next-flags v1.0.3
Feature Flag SDK for NextJS
This SDK provides a robust feature flagging solution for NextJS applications, allowing you to easily manage feature rollouts, conduct A/B tests, and implement user segmentation.
Table of Contents
Installation
To install the Feature Flag SDK, run the following command in your NextJS project directory:
npm install @occollective/next-flags
Setup
Storage Implementation
The SDK requires a storage solution to persist flags, experiments, and segments. You need to implement the Storage
interface provided by the SDK. Here's an example using a simple in-memory storage (Note: This is not suitable for production use):
// lib/featureFlagStorage.ts
import { Storage, Flag, Experiment, Segment } from '@your-org/feature-flag-sdk';
class InMemoryStorage implements Storage {
private flags: Map<string, Flag> = new Map();
private experiments: Map<string, Experiment> = new Map();
private segments: Map<string, Segment> = new Map();
async getFlag(id: string): Promise<Flag | null> {
return this.flags.get(id) || null;
}
async getAllFlags(): Promise<Flag[]> {
return Array.from(this.flags.values());
}
async saveFlag(flag: Flag): Promise<void> {
this.flags.set(flag.id, flag);
}
async deleteFlag(id: string): Promise<void> {
this.flags.delete(id);
}
// Implement similar methods for experiments and segments
// ...
}
export const storage = new InMemoryStorage();
For production use, implement a storage solution that persists data, such as a database adapter.
API Routes
Create API routes to manage flags, experiments, and segments. Place these files in the pages/api
directory of your NextJS project:
// pages/api/flags/index.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { FlagServer } from '@your-org/feature-flag-sdk';
import { storage } from '../../../lib/featureFlagStorage';
const flagServer = new FlagServer(storage);
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'GET') {
const flags = await flagServer.getAllFlags();
res.status(200).json(flags);
} else if (req.method === 'POST') {
const flag = await flagServer.createFlag(req.body);
res.status(201).json(flag);
} else {
res.status(405).end();
}
}
Create similar API routes for experiments and segments.
Middleware
To evaluate feature flags for each request, create a middleware file:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import {
FlagServer,
ExperimentServer,
SegmentServer,
evaluateFlags,
} from '@your-org/feature-flag-sdk';
import { storage } from './lib/featureFlagStorage';
const flagServer = new FlagServer(storage);
const experimentServer = new ExperimentServer(storage);
const segmentServer = new SegmentServer(storage);
export async function middleware(req: NextRequest) {
const userId = req.cookies.get('userId')?.value;
if (!userId) {
return NextResponse.next();
}
const flags = await flagServer.getAllFlags();
const experiments = await experimentServer.getAllExperiments();
const segments = await segmentServer.getAllSegments();
const userAttributes = JSON.parse(req.headers.get('x-user-attributes') || '{}');
const userFlags = evaluateFlags(flags, experiments, segments, {
userId,
attributes: userAttributes,
});
const response = NextResponse.next();
response.headers.set('x-feature-flags', JSON.stringify(userFlags));
return response;
}
export const config = {
matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
};
Usage
Creating and Managing Flags
Use the API routes you created to manage flags:
// Example: Creating a flag
const response = await fetch('/api/flags', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'new-feature',
description: 'A new feature flag',
type: 'boolean',
}),
});
const newFlag = await response.json();
Creating and Managing Experiments
Use the API routes you created to manage experiments:
// Example: Creating an experiment
const response = await fetch('/api/experiments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'button-color-test',
description: 'Testing different button colors',
flagId: 'existing-flag-id',
variations: [
{ name: 'blue', value: '#0000FF', trafficAllocation: 50 },
{ name: 'green', value: '#00FF00', trafficAllocation: 50 },
],
}),
});
const newExperiment = await response.json();
Creating and Managing Segments
Use the API routes you created to manage segments:
// Example: Creating a segment
const response = await fetch('/api/segments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'premium-users',
description: 'Users with premium subscription',
rules: [{ attribute: 'subscription', operator: 'equals', value: 'premium' }],
}),
});
const newSegment = await response.json();
Evaluating Flags
Flags are automatically evaluated in the middleware for each request. To access the evaluated flags in your NextJS pages or API routes:
import { NextPageContext } from 'next';
export async function getServerSideProps(context: NextPageContext) {
const featureFlags = JSON.parse(context.req.headers['x-feature-flags'] as string);
return {
props: {
featureFlags,
},
};
}
Client-Side Usage
To use feature flags on the client side, you can pass the evaluated flags from server-side props to your components:
import { useEffect, useState } from 'react';
export default function HomePage({ featureFlags }) {
const [flags, setFlags] = useState(featureFlags);
useEffect(() => {
// Optionally re-fetch flags periodically or on specific events
const refreshFlags = async () => {
const response = await fetch('/api/evaluate-flags');
const updatedFlags = await response.json();
setFlags(updatedFlags);
};
const intervalId = setInterval(refreshFlags, 60000); // Refresh every minute
return () => clearInterval(intervalId);
}, []);
return <div>{flags['new-feature'] && <NewFeatureComponent />}</div>;
}
Best Practices
- Use meaningful names for your flags, experiments, and segments.
- Keep flag evaluations as lightweight as possible to minimize impact on performance.
- Regularly clean up old or unused flags to keep your codebase clean.
- Use segments to create reusable user groups for targeted feature releases.
- Monitor and analyze the impact of your feature flags and experiments.
Troubleshooting
- If flags are not being evaluated, ensure that the middleware is correctly set up and that user IDs are being passed correctly.
- If experiments are not working as expected, check that the traffic allocation percentages add up to 100% and that the flag associated with the experiment exists.
- For any unexpected behavior, check the server logs for error messages or unexpected input.
API Reference
For a complete API reference, including all available methods and their parameters, please refer to the API documentation.