Skip to content

Commit

Permalink
use integration for feature-flags-split example (#732)
Browse files Browse the repository at this point in the history
### Description

Uses the upcoming integration for the feature-flags-split example.

The feature flags will now be bootstrapped from Edge Config.


For this demo I also had to copy the layout into the app router, since
`@vercel/example-ui` does not yet support app router. The launchdarkly
example suffers from the same issue. Once that's supported in
`@vercel/example-ui` we can just delete the components I had to copy
over from the template. cc @lfades

### Demo URL

https://feature-flags-split.vercel.app

### Type of Change

- [ ] New Example
- [x] Example updates (Bug fixes, new features, etc.)
- [ ] Other (changes to the codebase, but not to examples)

### New Example Checklist

- [ ] 🛫 `npm run new-example` was used to create the example
- [ ] 📚 The template wasn't used but I carefuly read the [Adding a new
example](https://github.com/vercel/examples#adding-a-new-example) steps
and implemented them in the example
- [ ] 📱 Is it responsive? Are mobile and tablets considered?
  • Loading branch information
dferber90 authored Jul 14, 2023
1 parent aadce63 commit 98f3e8b
Show file tree
Hide file tree
Showing 28 changed files with 650 additions and 381 deletions.
13 changes: 6 additions & 7 deletions edge-middleware/feature-flag-split/.env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# This API should have access to "All environments", you can create it under
# Admin Settings -> Workspace settings -> API keys
SPLIT_ADMIN_API_KEY =
SPLIT_WORKSPACE_ID =
# For new Split accounts you'll have two options here: "Prod-Default" and "Staging-Default"
SPLIT_ENVIRONMENT_ID = "Prod-Default"
# This is the SDK key (aka client-side key) that matches your environment id
NEXT_PUBLIC_SPLIT_SDK_CLIENT_API_KEY =
SPLIT_SDK_CLIENT_API_KEY=
# This is the connection string for the Edge Config the Split.io integration syncs its values into
EDGE_CONFIG=
# This is the key under which the Split.io integration syncs its values into Edge Config
# You can find it on the "Configure" page of your Split.io integration installation on vercel.com
EDGE_CONFIG_SPLIT_ITEM_KEY=
25 changes: 21 additions & 4 deletions edge-middleware/feature-flag-split/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ description: Learn to use Split, a feature delivery platform that powers feature
framework: Next.js
useCase: Edge Middleware
css: Tailwind
deployUrl: https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/edge-middleware/feature-flag-split&env=SPLIT_ADMIN_API_KEY,SPLIT_WORKSPACE_ID,SPLIT_ENVIRONMENT_ID,NEXT_PUBLIC_SPLIT_SDK_CLIENT_API_KEY&project-name=feature-flag-split&repository-name=feature-flag-split
demoUrl: https://edge-functions-feature-flag-split.vercel.app
deployUrl: https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/edge-middleware/feature-flag-split&env=SPLIT_WORKSPACE_ID,SPLIT_SDK_CLIENT_API_KEY,EDGE_CONFIG&project-name=feature-flag-split&repository-name=feature-flag-split&integration-ids=oac_bic40oWF5k9pDFboJhKYqMd1&edge-config-stores=%7B%22EDGE_CONFIG%22%3A%7B%7D%7D
demoUrl: https://feature-flag-split.vercel.app
relatedTemplates:
- ab-testing-simple
---
Expand All @@ -17,7 +17,7 @@ relatedTemplates:

## Demo

https://edge-functions-feature-flag-split.vercel.app
https://feature-flag-split.vercel.app

## How to Use

Expand All @@ -27,7 +27,7 @@ You can choose from one of the following two methods to use this repository:

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/edge-middleware/feature-flag-split&env=SPLIT_ADMIN_API_KEY,SPLIT_WORKSPACE_ID,SPLIT_ENVIRONMENT_ID,NEXT_PUBLIC_SPLIT_SDK_CLIENT_API_KEY&project-name=feature-flag-split&repository-name=feature-flag-split)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/edge-middleware/feature-flag-split&env=SPLIT_SDK_CLIENT_API_KEY,EDGE_CONFIG,EDGE_CONFIG_SPLIT_ITEM_KEY&project-name=feature-flag-split&repository-name=feature-flag-split&integration-ids=oac_bic40oWF5k9pDFboJhKYqMd1&edge-config-stores=%7B%22EDGE_CONFIG%22%3A%7B%7D%7D)

### Clone and Deploy

Expand All @@ -45,6 +45,23 @@ cp .env.example .env.local

Then open `.env.local` and set the environment variables to match the ones in your Split dashboard. Your keys should be available under Workspace settings - API Keys, in the admin settings of your organization.

#### Set up environment variables

Copy [.env.example](./env.example) to `.env.local`:

```bash
cp .env.example .env.local
```

Then fill it with the following information:

- Log in to the [Split console](https://app.split.io/login) and navigate to **Profile -> Admin Settings -> API Keys -> SDK API Keys** to retreive your SDK API Key.

- Install the [Split Vercel Integration](https://vercel.com/integrations/split) for your project.
Then fill in `.env.local` with the provided Edge Config Item Key.

- You can find the Edge Config Connection String on vercel.com -> Storage -> \[Your Edge Config\] -> Projects. You can click Connect Project if your Edge Config is not connected to any project yet. This will automatically create a token for you and set it up as an environment variable on your project. Note that you still need to provide it to your `.env.local` file. Otherwise, click on Tokens in the sidebar and find the token you want to use. Then click on the three dots of and select Copy Connection String.

Next, run Next.js in development mode:

```bash
Expand Down
17 changes: 17 additions & 0 deletions edge-middleware/feature-flag-split/app/about/a.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'
import { Page, Text, Link } from '@vercel/examples-ui'

export function AboutA() {
return (
<Page>
<Text variant="h2" className="mb-6">
About page
</Text>
<Text className="text-lg mb-4">This is the original about page</Text>
<Text className="mb-4">
You&apos;re currently on <b>/about</b>
</Text>
<Link href="/">Go back to /</Link>
</Page>
)
}
17 changes: 17 additions & 0 deletions edge-middleware/feature-flag-split/app/about/b.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'
import { Page, Text, Code, Link } from '@vercel/examples-ui'

export function AboutB() {
return (
<Page>
<Text variant="h2" className="mb-6">
About Variant
</Text>
<Text className="text-lg mb-4">
You&apos;re currently looking at the variant B of the about page under{' '}
<Code>app/about/b.tsx</Code>
</Text>
<Link href="/">Go back to /</Link>
</Page>
)
}
18 changes: 18 additions & 0 deletions edge-middleware/feature-flag-split/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { cookies } from 'next/headers'
import { AboutA } from './a'
import { AboutB } from './b'
import { createSplitClient } from '@lib/split'

// export const runtime = 'edge'
export const dynamic = 'force-dynamic'
export const config = { runtime: 'edge' }

export default async function About() {
const userKey = cookies().get('split-userkey')?.value ?? 'anonymous'
const client = await createSplitClient(userKey)
const treatment = await client.getTreatment('New_About_Page')

await client.destroy() // TODO can we use waitUntil(client.destroy()) somehow?

return treatment === 'on' ? <AboutB /> : <AboutA />
}
49 changes: 49 additions & 0 deletions edge-middleware/feature-flag-split/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import '@vercel/examples-ui/globals.css'
import Vercel from '../components/vercel-icon'
import Nav from '../components/nav'

export const metadata = {
title: 'feature-flag-split - Vercel Examples',
description: 'An example showing how to use Vercel with Split.io',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
const path = 'edge-middleware/feature-flag-split'
return (
<html lang="en">
<body>
<div className="mx-auto h-screen flex flex-col">
<Nav
path={path}
deployButton={{
customDeployUrl:
'https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/edge-middleware/feature-flag-split&env=SPLIT_SDK_CLIENT_API_KEY,EDGE_CONFIG,EDGE_CONFIG_SPLIT_ITEM_KEY&project-name=feature-flag-split&repository-name=feature-flag-split&integration-ids=oac_bic40oWF5k9pDFboJhKYqMd1&edge-config-stores=%7B%22EDGE_CONFIG%22%3A%7B%7D%7D',
}}
/>

<div className="px-8 bg-accents-0">{children}</div>

<footer className="py-10 w-full mt-auto border-t flex items-center justify-center bg-accents-1 z-20">
<span className="text-primary">Created by</span>
<a
href="https://vercel.com"
aria-label="Vercel.com Link"
target="_blank"
rel="noreferrer"
className="text-black"
>
<Vercel
className="inline-block h-6 ml-3 text-primary"
alt="Vercel.com Logo"
/>
</a>
</footer>
</div>
</body>
</html>
)
}
17 changes: 17 additions & 0 deletions edge-middleware/feature-flag-split/app/marketing/a.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'
import { Page, Text, Link } from '@vercel/examples-ui'

export function MarketingA() {
return (
<Page>
<Text variant="h2" className="mb-6">
Marketing page
</Text>
<Text className="text-lg mb-4">This is the original marketing page</Text>
<Text className="mb-4">
You&apos;re currently on <b>/marketing</b>
</Text>
<Link href="/">Go back to /</Link>
</Page>
)
}
17 changes: 17 additions & 0 deletions edge-middleware/feature-flag-split/app/marketing/b.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'
import { Page, Text, Link, Code } from '@vercel/examples-ui'

export function MarketingB() {
return (
<Page>
<Text variant="h2" className="mb-6">
Marketing Variant
</Text>
<Text className="text-lg mb-4">
You&apos;re currently looking at the variant of the marketing page under{' '}
<Code>app/marketing/b.tsx</Code>
</Text>
<Link href="/">Go back to /</Link>
</Page>
)
}
18 changes: 18 additions & 0 deletions edge-middleware/feature-flag-split/app/marketing/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { cookies } from 'next/headers'
import { MarketingA } from './a'
import { MarketingB } from './b'
import { createSplitClient } from '@lib/split'

// export const runtime = 'edge'
export const dynamic = 'force-dynamic'
export const config = { runtime: 'edge' }

export default async function Marketing() {
const userKey = cookies().get('split-userkey')?.value ?? 'anonymous'
const client = await createSplitClient(userKey)
const treatment = await client.getTreatment('New_Marketing_Page')

await client.destroy() // TODO can we use waitUntil(client.destroy()) somehow?

return treatment === 'on' ? <MarketingB /> : <MarketingA />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* Heroicon name: outline/exclamation-triangle */
function ExclamantionTriangleIcon() {
return (
<svg
className="h-6 w-6 text-gray-600"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 10.5v3.75m-9.303 3.376C1.83 19.126 2.914 21 4.645 21h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 4.88c-.866-1.501-3.032-1.501-3.898 0L2.697 17.626zM12 17.25h.007v.008H12v-.008z"
/>
</svg>
)
}

export default function MissingEdgeConfigDialog() {
return (
<div
className="relative z-10"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div className="relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<div className="sm:flex sm:items-start">
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-gray-100 sm:mx-0 sm:h-10 sm:w-10">
<ExclamantionTriangleIcon />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3
className="text-lg font-medium leading-6 text-gray-900"
id="modal-title"
>
Incomplete Environment Variables Setup
</h3>
<div className="mt-2">
<p className="text-sm text-gray-500">
Follow these steps to finish the setup of this example:
</p>
<ol className="text-sm text-gray-500 list-disc ml-8 mt-2 flex gap-2 flex-col">
<li className="list-item list-disc">
Create an Edge Config and connect it to this project and
store its connection string under the{' '}
<span className="bg-gray-100 p-1 text-gray-900 rounded">
EDGE_CONFIG
</span>{' '}
environment variable
</li>
<li className="list-item list-disc">
Ensure you have the{' '}
<span className="bg-gray-100 p-1 text-gray-900 rounded">
EDGE_CONFIG_SPLIT_ITEM_KEY
</span>{' '}
environment variable configured and it contains the item
key as specified by the Split integration. You can find
this key on your account at Vercel under Integrations &gt;
Split &gt; Manage &gt; Configure &gt; Edge Config Item Key
</li>
<li className="list-item list-disc">
Pull your latest Environment Variables if you are
developing locally
</li>
<li className="list-item list-disc">
Restart or redeploy your application
</li>
</ol>
<p className="text-sm text-gray-500 mt-2">
Then reload the page and this dialog will go away if your
setup is correct.
</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:ml-10 sm:flex sm:pl-4">
<a
href="https://github.com/vercel/examples/blob/main/edge-middleware/feature-flag-split/README.md#set-up-environment-variables"
target="_blank"
rel="noopener noreferrer"
className="inline-flex w-full justify-center rounded-md border border-transparent bg-gray-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 sm:w-auto sm:text-sm"
>
Open Documentation
</a>
</div>
</div>
</div>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
'use client'
import { Button, List, Page, Text, Link } from '@vercel/examples-ui'
import Cookies from 'js-cookie'
import { Layout, Page, Text, List, Link, Button } from '@vercel/examples-ui'

export default function Index() {
const removeCookie = (name: string) => {
Cookies.remove(name)
window.location.reload()
}

return (
<Page>
<Text variant="h2" className="mb-6">
Expand All @@ -29,22 +25,28 @@ export default function Index() {
variant has a 50% chance)
</Text>
<div>
<Button
variant="secondary"
className="mr-2.5"
onClick={() => removeCookie(`flag-about`)}
>
Remove /about cookie & reload
</Button>
<Button
variant="secondary"
onClick={() => removeCookie(`flag-marketing`)}
>
Remove /marketing cookie & reload
</Button>
<CookieButton userKey="Joe" />
<CookieButton userKey="Bobby" />
</div>
</Page>
)
}

Index.Layout = Layout
function CookieButton({ userKey }: { userKey: string }) {
'use client'

const removeCookie = (name: string) => {
Cookies.set('split-userkey', userKey)
window.location.reload()
}

return (
<Button
variant="secondary"
className="mr-2.5"
onClick={() => removeCookie('split-userkey')}
>
Authenticate as {userKey} and reload page
</Button>
)
}
Loading

21 comments on commit 98f3e8b

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

cron – ./solutions/cron

cron-template.vercel.app
cron-git-main-vercel-labs.vercel.app
cron-vercel-labs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

postgres-kysely – ./storage/postgres-kysely

postgres-kysely.vercel.app
postgres-kysely-vercel-labs.vercel.app
postgres-kysely-git-main-vercel-labs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

next-flask – ./python/nextjs-flask

nextjs-flask-starter.vercel.app
next-flask-vercel-labs.vercel.app
nextjs-python.vercel.app
next-flask-git-main-vercel-labs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

edge-ab-testing-statsig – ./edge-middleware/ab-testing-statsig

edge-ab-testing-statsig-now-examples.vercel.app
edge-ab-testing-statsig.vercel.app
edge-ab-testing-statsig-git-main-now-examples.vercel.app
edge-ab-testing-statsig.vercel.sh

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-static-files – ./build-output-api/static-files

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-image-optimization – ./build-output-api/image-optimization

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-overrides – ./build-output-api/overrides

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-serverless-functions – ./build-output-api/serverless-functions

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-routes – ./build-output-api/routes

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-draft-mode – ./build-output-api/draft-mode

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-isr – ./build-output-api/on-demand-isr

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-edge-functions – ./build-output-api/edge-functions

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-edge-middleware – ./build-output-api/edge-middleware

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-wildcard – ./build-output-api/wildcard

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

edge-api-routes-json-response – ./edge-api-routes/json-response

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

edge-api-routes-hello-world – ./edge-api-routes/hello-world

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

edge-api-routes-cache-control – ./edge-api-routes/cache-control

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

edge-api-routes-query-parameters – ./edge-api-routes/query-parameters

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

build-output-api-prerender-functions – ./build-output-api/prerender-functions

@vercel
Copy link

@vercel vercel bot commented on 98f3e8b Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks for Deployment have failed

edge-functions-authed-proxy – ./edge-api-routes/authed-proxy

Please sign in to comment.