# Flagship + Vercel Edge Function Integration

{% hint style="info" %}
📘 Github Repository

<https://github.com/flagship-io/flagship-vercel-edge-function-example>
{% endhint %}

### Overview

This guide shows how to:

* Initialize the Flagship SDK in a Vercel Edge Function
* Use Edge Config for caching bucketing data to improve performance
* Create a visitor object with context data from request headers
* Fetch feature flags assigned to this visitor
* Retrieve specific flag values for use in the application
* Send analytics data back to Flagship
* Ensure analytics are sent before the function terminates

### Prerequisites

* [Node.js](https://nodejs.org/) (v18 or later)
* [Yarn](https://yarnpkg.com/) (v4 or later)
* A [Vercel account](https://vercel.com/signup)
* A [Flagship account](https://app.flagship.io/login) with API credentials

### Setup

1. Create a Next.js project for Vercel Edge Functions:

```shell
npx create-next-app@latest my-flagship-app
cd my-flagship-app
```

2. Install dependencies:

```shell
yarn add @flagship.io/js-sdk @vercel/edge-config
```

3. Configure your Flagship credentials as environment variables:

```shell
# Create or update .env.local file
echo "FLAGSHIP_ENV_ID=your_env_id" > .env.local
echo "FLAGSHIP_API_KEY=your_api_key" >> .env.local
```

4. Create an Edge Config in the Vercel dashboard and link it to your project:

```shell
npx vercel link
npx vercel env pull .env.local
```

### Use Edge Config or direct integration for bucketing data

Bucketing data contains information about your Flagship campaigns and variations, allowing the Edge Function to make flag decisions without calling the Flagship API for every request.

#### Development Approach

**Option 1: Edge Config Storage (Recommended)**

1. Fetch bucketing data directly from the Flagship CDN:

```shell
# Replace YOUR_ENV_ID with your Flagship Environment ID
curl -s https://cdn.flagship.io/YOUR_ENV_ID/bucketing.json > bucketing-data.json
```

2. Upload the bucketing data to your Vercel Edge Config:

Adding the bucketing data to Edge config can be done using the Vercel [dashboard](https://vercel.com/docs/edge-config/edge-config-dashboard) or via the Vercel API.

Here’s how to do it via the API:

```shell
# Using Vercel API to upload the bucketing data
curl -X PATCH "https://api.vercel.com/v1/edge-config/ecfg_YOUR_EDGE_CONFIG_ID/items" \
  -H "Authorization: Bearer YOUR_VERCEL_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "operation": "create",
        "key": "initialBucketing",
        "value": '"$(cat bucketing-data.json | jq -c .)"'
      }
    ]
  }'
```

3. Access this data in your code:

```typescript
import { get } from "@vercel/edge-config";
const initialBucketing = await get("initialBucketing");
```

**Option 2: Direct Integration**

For direct integration, you can:

1. Fetch the bucketing data during your build process
2. Save it as a JSON file in your project
3. Import it directly in your Edge Function

```shell
# During build/deployment:
curl -s https://cdn.flagship.io/YOUR_ENV_ID/bucketing.json > src/bucketing-data.json
```

Then import in your code:

```typescript
import bucketingData from './bucketing-data.json';
// Use this data when initializing Flagship
```

#### Production Approach

For production environments, there are two recommended approaches. Both require setting up webhooks in the Flagship platform that trigger your CI/CD pipeline when campaigns are updated.\
Find more details here.

### Initialize the Flagship SDK in a Vercel Edge Function

The first step to using Flagship in your Vercel Edge Function is to initialize the SDK. This sets up the connection with your Flagship project and configures how feature flags will be delivered.

#### With Edge Config Storage

```typescript
import Flagship, { 
  BucketingDTO, 
  DecisionMode, 
  LogLevel 
} from "@flagship.io/js-sdk/dist/edge.js";
import { get } from "@vercel/edge-config";

export const runtime = "edge";

async function initFlagship(request: Request) {
  // Access Flagship credentials from environment variables
  const { FLAGSHIP_ENV_ID, FLAGSHIP_API_KEY } = process.env;

  // Retrieve cached bucketing data from edge config (improves performance)
  const initialBucketing = await get("initialBucketing");

  // Initialize Flagship SDK with credentials and configuration
  await Flagship.start(FLAGSHIP_ENV_ID as string, FLAGSHIP_API_KEY as string, {
    // Use edge bucketing mode for optimal performance in serverless environments
    decisionMode: DecisionMode.BUCKETING_EDGE,
    // Pass cached bucketing data
    initialBucketing: initialBucketing as BucketingDTO,
    // Defer fetching campaign data until explicitly needed
    fetchNow: false,
    logLevel: LogLevel.DEBUG,
  });

  // Continue with the rest of your function logic...
}
```

#### With Direct Integration

```typescript
import Flagship, { 
  BucketingDTO, 
  DecisionMode, 
  LogLevel 
} from "@flagship.io/js-sdk/dist/edge.js";
// Import bucketing data directly
import initialBucketing from './bucketing-data.json';

export const runtime = "edge";

async function initFlagship(request: Request) {
  // Access Flagship credentials from environment variables
  const { FLAGSHIP_ENV_ID, FLAGSHIP_API_KEY } = process.env;

  // Initialize Flagship SDK with credentials and embedded bucketing data
  await Flagship.start(FLAGSHIP_ENV_ID as string, FLAGSHIP_API_KEY as string, {
    // Use edge bucketing mode for optimal performance in serverless environments
    decisionMode: DecisionMode.BUCKETING_EDGE,
    // Use the imported bucketing data
    initialBucketing: initialBucketing as BucketingDTO,
    // Defer fetching campaign data until explicitly needed
    fetchNow: false,
    logLevel: LogLevel.DEBUG,
  });

  // Continue with the rest of your function logic...
}
```

#### Configuration Options

* **decisionMode**:
  * `BUCKETING_EDGE` is recommended for Edge Functions as it makes decisions locally using bucketing data
  * `API` mode would call Flagship servers for each decision (not recommended for Edge Functions)
* **initialBucketing**:
  * Pre-loaded campaign data to make local decisions without API calls
  * Retrieved from Edge Config or embedded in your code
* **fetchNow**:
  * `false` Defer fetching campaign data until explicitly needed

### Create a visitor object with context data from request headers or any other source

The visitor object represents a user of your application. You need to create one for each request, providing a unique ID and relevant context data that can be used for targeting.

```typescript
// From the edge function handler
const { searchParams } = new URL(request.url);

// Get visitor ID from query params or let SDK generate one
const visitorId = (searchParams.get('visitorId') as string) || undefined;

// Create a visitor with context data extracted from request headers
// This context can be used for targeting rules in Flagship campaigns
const visitor = Flagship.newVisitor({
  visitorId,
  // Set GDPR consent status for data collection
  hasConsented: true,
  context: {
    userAgent: request.headers.get('user-agent') || 'unknown',
    path: request.url,
    referrer: request.headers.get('referer') || 'unknown',
    // You can add any additional context data that's relevant for your targeting
    // For example:
    // isPremiumUser: searchParams.get('premium') === 'true',
    // deviceType: detectDeviceType(request.headers.get('user-agent')),
  },
});
```

You can include any information in the context object that might be useful for targeting. Common examples include:

* **Demographics**: age, gender, location
* **Technical**: device, browser, OS, screen size
* **Behavioral**: account type, subscription status
* **Custom**: any application-specific attributes

This context is used by Flagship for targeting rules, so include any attributes that might be useful for segmenting your users.

### Fetch feature flags assigned to this visitor

Once you have a visitor object, you need to fetch the feature flags assigned to them based on targeting rules:

```typescript
// Fetch feature flags assigned to this visitor
// This applies all targeting rules based on visitor context
await visitor.fetchFlags();

// ... Continue with the rest of your function logic
```

This operation evaluates all campaign rules against the visitor's context and assigns flag variations accordingly. With edge bucketing, this happens locally without any network requests.

### Retrieve specific flag values for use in the application

After fetching flags, you can retrieve specific flag values for use in your application. The SDK provides a type-safe way to access flag values with default fallbacks.

```typescript
// Retrieve specific flag values with default fallbacks if flags aren't defined
const welcomeMessage = visitor
  .getFlag("welcome_message")
  .getValue("Welcome to our site!");
const isFeatureEnabled = visitor
  .getFlag("new_feature_enabled")
  .getValue(false);

// You can get different types of values:
// Strings
const title = visitor.getFlag('page_title').getValue('Default Title');

// Numbers
const discountPercent = visitor.getFlag('discount_percentage').getValue(0);

// Objects
const uiConfig = visitor.getFlag('ui_config').getValue({
  theme: 'light',
  showBanner: false,
  menuItems: ['home', 'products', 'contact'],
});

// Arrays
const items = visitor.getFlag('menu_items').getValue(['home', 'about']);
```

Always provide a default value that matches the expected type. This ensures your application works even if the flag isn't defined or there's an issue fetching flags.

{% hint style="info" %}
**Note:** calling `getValue` automatically activates the flag, meaning it will be counted in the reporting.
{% endhint %}

### Send analytics data back to Flagship

To measure the impact of your feature flags, you need to send analytics data back to Flagship. This includes page views, conversions, transactions, and custom events.

```typescript
import { EventCategory, HitType } from "@flagship.io/js-sdk/dist/edge.js";

// Send analytics data back to Flagship for campaign reporting
visitor.sendHits([
  {
    type: HitType.PAGE_VIEW,
    documentLocation: request.url,
  },
  {
    type: HitType.EVENT,
    category: EventCategory.ACTION_TRACKING,
    action: "feature_view",
    label: "new_feature",
    value: isFeatureEnabled ? 1 : 0,
  },
]);
```

Analytics data is crucial for measuring the impact of your feature flags in A/B testing scenarios. You can track page views, events, transactions, and more.

### Ensure analytics are sent before the function terminates

Vercel Edge Functions can terminate quickly, potentially before analytics data is sent. To prevent this, use the Next.js `after` function:

```typescript
import { after } from "next/server";

// Ensure analytics are sent before the function terminates
after(async () => {
  await Flagship.close();
});

// Return feature flag values as JSON response
return Response.json({
  message: welcomeMessage,
  features: {
    newFeatureEnabled: isFeatureEnabled,
  },
});
```

This ensures that all pending analytics are sent before the function terminates, giving you accurate reporting data.

### Complete Example

Please refer to the complete example in the [GitHub repository](https://github.com/flagship-io/flagship-vercel-edge-function-example).

### Production Approach to retrieve and update bucketing data

For production environments, there are two recommended approaches. Both require setting up webhooks in the Flagship platform that trigger your CI/CD pipeline when campaigns are updated:

**Common Setup for Both Approaches**

1. Set up a webhook in the Flagship Platform that triggers whenever a campaign is updated
2. Configure the webhook to call your CI/CD pipeline or serverless function

The primary difference between the approaches is where the bucketing data is stored:

**Option 1: Webhook + Edge Config Storage**

This approach stores bucketing data in Vercel Edge Config:

```yaml
name: Upload Flagship Bucketing Data

on:
  repository_dispatch:
    types: [flagship-campaign-updated]

jobs:
  update-bucketing:
    runs-on: ubuntu-latest
    steps:
      - name: Fetch latest bucketing data
        run: |
          curl -s https://cdn.flagship.io/${{ secrets.FLAGSHIP_ENV_ID }}/bucketing.json > bucketing-data.json

      - name: Update Edge Config via API
        run: |
          curl -X PATCH "https://api.vercel.com/v1/edge-config/${{ secrets.EDGE_CONFIG_ID }}/items" \
            -H "Authorization: Bearer ${{ secrets.VERCEL_API_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{
              "items": [
                {
                  "operation": "create",
                  "key": "initialBucketing",
                  "value": '"$(cat bucketing-data.json | jq -c .)"'
                }
              ]
            }'
```

**Option 2: Direct Integration via Deployment**

This approach embeds bucketing data directly in your function code:

```yaml
name: Deploy Function with Latest Bucketing Data

on:
  repository_dispatch:
    types: [flagship-campaign-updated]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Fetch latest bucketing data
        run: |
          curl -s https://cdn.flagship.io/${{ secrets.FLAGSHIP_ENV_ID }}/bucketing.json > src/bucketing-data.json

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install dependencies
        run: yarn install

      - name: Deploy to Vercel
        run: npx vercel deploy --prod
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
```

**Trade-offs between approaches:**

**Edge Config Storage Approach:**

* **Performance**: Adds Edge Config read latency to each request
* **Flexibility**: Allows updating flags without redeploying code
* **Reliability**: If Edge Config is unavailable, flags might not work correctly
* **Debugging**: Easier to inspect current bucketing data separately from code
* **Isolation**: Clearer separation between code and configuration

**Direct Integration Approach:**

* **Performance**: Faster initialization with no external calls during startup
* **Deployment**: Requires redeployment for each flag configuration change
* **Reliability**: Fewer runtime dependencies, more predictable behavior
* **Bundle size**: Larger function bundle due to embedded bucketing data
* **Caching**: Better cold start performance since data is bundled

Choose the approach that best fits your deployment frequency and performance requirements.

### Learn More

* [Vercel Edge Functions Documentation](https://vercel.com/docs/functions/edge-functions)
* [Vercel Edge Config Documentation](https://vercel.com/docs/storage/edge-config)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.abtasty.com/server-side/concepts/flagship-edge-worker-integration/flagship-vercel-edge-function-integration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
