optimizely-redirect-middleware
v0.1.0
Published
Lightweight, intentionally-temporary Next.js (Vercel) middleware for Optimizely Feature Experimentation redirect routing. Matches request paths against a local JSON config, decides a flag via a user context, and 307-redirects to the winning variation's UR
Maintainers
Readme
optimizely-redirect-middleware
Lightweight, intentionally-temporary Next.js middleware for running Optimizely Feature Experimentation redirect tests on Vercel's Edge Runtime.
Note: This package is not intended to replace a full Optimizely SDK integration. It is a temporary redirect layer that complements broader Next.js / React SDK usage.
Features
- ✅ Match request paths (exact, prefix, regex)
- ✅ Evaluate Optimizely decisions via user context API
- ✅ Redirect to variation-specific URLs
- ✅ Preserve all original query parameters
- ✅ Set analytics cookies with experiment metadata
- ✅ Sticky bucketing via identity cookie
- ✅ Custom user ID and attribute resolution per request
- ✅ Edge Runtime compatible (no Node.js APIs required)
- ✅ Fail-open design — errors pass through to original page
Installation
npm install optimizely-redirect-middleware @optimizely/optimizely-sdkDatafile Setup
Download your Optimizely datafile and commit it to your project:
curl -o optimizely-datafile.json https://cdn.optimizely.com/datafiles/YOUR_SDK_KEY.jsonFind your SDK Key in: Optimizely Dashboard → Settings → Environments → SDK Key
Note: The deployed app uses this static file. To pick up experiment changes from the Optimizely dashboard, re-download the datafile and re-deploy.
Quick Start
// middleware.ts (in the consumer's Next.js app)
import { NextRequest } from 'next/server';
import { createRedirectMiddleware, getOptimizelyClient } from 'optimizely-redirect-middleware';
import type { RedirectConfig } from 'optimizely-redirect-middleware';
import datafile from './optimizely-datafile.json';
const config: RedirectConfig = {
experiments: [
{
flagKey: 'pricing_page_redirect',
path: '/pricing',
variations: {
new_pricing: '/pricing-v2',
promo_pricing: 'https://promo.example.com/pricing',
},
},
],
};
const handleRedirect = createRedirectMiddleware({
config,
getClient: () => getOptimizelyClient(datafile)!,
getAttributes: (req) => ({
country: req.headers.get('x-vercel-ip-country') || 'US',
}),
});
export function middleware(request: NextRequest) {
return handleRedirect(request);
}
export const config = { matcher: ['/pricing'] };API
createRedirectMiddleware(options)
Returns a middleware function (request: NextRequest) => NextResponse.
options
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| config | RedirectConfig | ✅ | Experiment definitions |
| getClient | () => OptimizelyClientLike | ✅ | Returns an Optimizely client instance |
| getUserId | (req) => string | | Custom user ID resolver |
| getAttributes | (req) => object | | Custom attribute resolver for targeting |
| onError | (error, { flagKey }) => void | | Error handler (fails open by default) |
| redirectStatus | number | | HTTP status code (default: 307) |
getOptimizelyClient(datafile)
Convenience helper that creates a cached, Edge-safe Optimizely client from a datafile.
import { getOptimizelyClient } from 'optimizely-redirect-middleware';
import datafile from './optimizely-datafile.json';
const client = getOptimizelyClient(datafile)!;ExperimentRule
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| flagKey | string | ✅ | Optimizely flag key |
| path | string | ✅ | URL path to match |
| matchType | 'exact' \| 'prefix' \| 'regex' | | Path matching strategy (default: exact) |
| enabled | boolean | | Set false to skip (default: true) |
| attributes | Record<string, string \| number \| boolean \| null> | | Experiment-level targeting attributes |
| variations | Record<string, string> | ✅ | Variation key → destination URL map |
Cookies
Two cookies are set on redirect:
Identity cookie (opti_uid)
Stores the user ID for sticky bucketing. Only set for new users.
Metadata cookie (opti_exp)
Stores experiment decision metadata for analytics:
{
"flagKey": "pricing_page_redirect",
"variationKey": "new_pricing",
"ruleKey": "pricing_ab",
"userId": "uuid-here",
"decidedAt": "2026-07-10T00:00:00.000Z"
}Keeping the Datafile Fresh
Since the datafile is static, experiment changes in the Optimizely dashboard won't take effect until you re-deploy:
# Re-download and deploy
curl -o optimizely-datafile.json https://cdn.optimizely.com/datafiles/YOUR_SDK_KEY.json
npm run build && npm run deployTip: Set up an Optimizely webhook to trigger a Vercel deploy hook for automatic re-deploys.
Utility Exports
| Export | Description |
|--------|-------------|
| findExperiment(config, path) | Find matching experiment for a path |
| normalizePath(path) | Normalize URL paths |
| buildDestinationUrl(dest, originalUrl) | Build redirect URL with merged query params |
| encodeExperimentCookie(payload) | Serialize experiment metadata |
| decodeExperimentCookie(value) | Parse experiment metadata cookie |
References
License
MIT
