1.0.3 • Published 11 months ago

@occollective/next-flags v1.0.3

Weekly downloads
-
License
ISC
Repository
-
Last release
11 months ago

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

  1. Installation
  2. Setup
  3. Usage
  4. Client-Side Usage
  5. Best Practices
  6. Troubleshooting
  7. API Reference

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

  1. Use meaningful names for your flags, experiments, and segments.
  2. Keep flag evaluations as lightweight as possible to minimize impact on performance.
  3. Regularly clean up old or unused flags to keep your codebase clean.
  4. Use segments to create reusable user groups for targeted feature releases.
  5. 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.

1.0.3

11 months ago

1.0.2

11 months ago

1.0.1

11 months ago

1.0.0

11 months ago