Skip to content

Commit d25f327

Browse files
authored
feat: add new Vercel example (#130)
This PR adds a new Vercel example that showcases the following: 1. Bootstrapping feature flags from the edge runtime and consuming them in the [LaunchDarkly Client-side SDK for React](https://github.com/launchdarkly/react-client-sdk). This is leveraging feature flags in edge-rendered pages while still maintaining the events and ergonomics provided by the React SDK. You can see details in `app/layout.tsx` and `components/launchdarklyProvider.tsx`. 2. Evaluating feature flags in the [Edge Middleware](https://vercel.com/docs/concepts/functions/edge-middleware). This can be seen in `middleware.ts`. I'm open to suggestions on new names for the examples.
1 parent 2296c4f commit d25f327

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+661
-13
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = {
88
project: './tsconfig.eslint.json',
99
},
1010
plugins: ['@typescript-eslint', 'prettier'],
11-
ignorePatterns: ['**/dist/**'],
11+
ignorePatterns: ['**/dist/**', '**/vercel/examples/**'],
1212
rules: {
1313
'prettier/prettier': ['error'],
1414
'class-methods-use-this': 'off',

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"packages/sdk/cloudflare",
99
"packages/sdk/cloudflare/example",
1010
"packages/sdk/vercel",
11-
"packages/sdk/vercel/example-vercel",
11+
"packages/sdk/vercel/examples/complete",
12+
"packages/sdk/vercel/examples/route-handler",
1213
"packages/sdk/akamai"
1314
],
1415
"private": true,

packages/sdk/vercel/README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66
[![NPM][sdk-vercel-dm-badge]][sdk-vercel-npm-link]
77
[![NPM][sdk-vercel-dt-badge]][sdk-vercel-npm-link]
88

9-
This library supports using Vercel [Edge Config](https://vercel.com/docs/concepts/edge-network/edge-config) to replace the default in-memory feature store of the [LaunchDarkly Node.js SDK](https://github.com/launchdarkly/vercel-server-sdk).
9+
This library supports using Vercel [Edge Config](https://vercel.com/docs/concepts/edge-network/edge-config) to replace the default in-memory feature store of the [LaunchDarkly Node.js SDK](https://github.com/launchdarkly/node-server-sdk).
1010

11-
For more information, see the [SDK features guide](https://docs.launchdarkly.com/sdk/features/storing-data).
11+
## The LaunchDarkly Vercel integration is required
12+
13+
This SDK requires [LaunchDarkly's Vercel integration](https://docs.launchdarkly.com/integrations/vercel?q=verc) to push feature flag data into a Vercel Edge Config. The Vercel integration is available to customers on an Enterprise plan. To learn more, [read about our pricing](https://launchdarkly.com/pricing/). To upgrade your plan, [contact Sales](https://launchdarkly.com/contact-sales/).
14+
15+
For more information, see the [Vercel SDK reference](https://docs.launchdarkly.com/sdk/edge/vercel).
1216

1317
## Install
1418

@@ -22,16 +26,16 @@ or yarn:
2226
yarn add -D @launchdarkly/vercel-server-sdk
2327
```
2428

25-
## Quickstart
29+
## Quick start
2630

27-
Initialize the ldClient with the [Vercel Edge SDK](https://vercel.com/docs/concepts/edge-network/edge-config/edge-config-sdk) and your LaunchDarkly client side sdk key:
31+
Initialize the ldClient with the [Vercel Edge SDK](https://vercel.com/docs/concepts/edge-network/edge-config/edge-config-sdk) and your LaunchDarkly [client-side ID](https://docs.launchdarkly.com/sdk/concepts/client-side-server-side#client-side-id):
2832

2933
```typescript
30-
import init from '@launchdarkly/vercel-server-sdk';
34+
import { init } from '@launchdarkly/vercel-server-sdk';
3135
import { createClient } from '@vercel/edge-config';
3236

3337
const edgeClient = createClient(process.env.EDGE_CONFIG);
34-
const ldClient = init('YOUR CLIENT-SIDE SDK KEY', edgeClient);
38+
const ldClient = init('YOUR CLIENT-SIDE ID', edgeClient);
3539

3640
await ldClient.waitForInitialization();
3741
const ldContext = {
@@ -42,7 +46,7 @@ const ldContext = {
4246
const flagValue = await ldClient.variation('my-flag', ldContext, true);
4347
```
4448

45-
To learn more, head straight to the [complete reference guide for this SDK](https://docs.launchdarkly.com/sdk/server-side/vercel).
49+
To learn more, see the [examples](examples/README.md) in this repository or head straight to the [complete reference guide for this SDK](https://docs.launchdarkly.com/sdk/server-side/vercel).
4650

4751
## Developing this SDK
4852

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# LaunchDarkly Vercel Edge SDK examples
2+
3+
The following examples showcase using the LaunchDarkly Vercel Edge SDK to evaluate feature flags in Vercel's [Edge Runtime](https://vercel.com/docs/concepts/functions/edge-functions/edge-runtime). Both examples require the use of the [LaunchDarkly Vercel integration](https://docs.launchdarkly.com/integrations/vercel) to push feature flag data into Vercel's Edge Config.
4+
5+
## [Complete example](complete/README.md)
6+
7+
This example shows how to evaluate feature flags in Vercel's edge runtime using the [LaunchDarkly Vercel SDK](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/vercel). Two primary use cases are highlighted:
8+
9+
1. Bootstrapping feature flags from the edge runtime and consuming them in the [LaunchDarkly Client-side SDK for React](https://github.com/launchdarkly/react-client-sdk). This is leveraging feature flags in edge-rendered pages while still maintaining the events and ergonomics provided by the React SDK. You can see details in [`app/layout.tsx`](./app/layout.tsx) and [`components/launchdarklyProvider.tsx`](./components/launchdarklyProvider.tsx).
10+
2. Evaluating feature flags in the [Edge Middleware](https://vercel.com/docs/concepts/functions/edge-middleware). This can be seen in [`middleware.ts`](./middleware.ts).
11+
12+
### Demo
13+
14+
https://hello-vercel-edge.vercel.app/
15+
16+
## [Route Handler example](route-handler/README.md)
17+
18+
This is an example test app to showcase the usage of the Vercel LaunchDarkly
19+
SDK to evaluate a feature flag in a [Route Handler](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) using [Vercel's edge runtime](https://nextjs.org/docs/app/building-your-application/routing/router-handlers#edge-and-nodejs-runtimes).
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"root": true,
3+
"extends": "next/core-web-vitals"
4+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# Dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# Testing
9+
/coverage
10+
11+
# Next.js
12+
/.next/
13+
/out/
14+
15+
# VS code
16+
/.vscode
17+
18+
# Production
19+
/build
20+
21+
# Misc
22+
.DS_Store
23+
*.pem
24+
25+
# Debug
26+
npm-debug.log*
27+
yarn-debug.log*
28+
yarn-error.log*
29+
30+
# Local ENV files
31+
.env.local
32+
.env.development.local
33+
.env.test.local
34+
.env.production.local
35+
36+
# Vercel
37+
.vercel
38+
39+
# Turborepo
40+
.turbo
41+
42+
# typescript
43+
*.tsbuildinfo
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
legacy-peer-deps=true
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Complete example app for Vercel LaunchDarkly SDK
2+
3+
This example shows how to evaluate feature flags in Vercel's edge runtime using the [LaunchDarkly Vercel SDK](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/vercel). Two primary use cases are highlighted:
4+
5+
1. Bootstrapping feature flags from the edge runtime and consuming them in the [LaunchDarkly Client-side SDK for React](https://github.com/launchdarkly/react-client-sdk). This is leveraging feature flags in edge-rendered pages while still maintaining the events and ergonomics provided by the React SDK. You can see details in [`app/layout.tsx`](./app/layout.tsx) and [`components/launchdarklyProvider.tsx`](./components/launchdarklyProvider.tsx).
6+
2. Evaluating feature flags in the [Edge Middleware](https://vercel.com/docs/concepts/functions/edge-middleware). This can be seen in [`middleware.ts`](./middleware.ts).
7+
8+
## Demo
9+
10+
https://hello-vercel-edge.vercel.app/
11+
12+
## Local development
13+
14+
#### Create a new LaunchDarkly project and flags
15+
16+
For simplicity, we recommend [creating a new LaunchDarkly project](https://docs.launchdarkly.com/home/organize/projects/?q=create+proj) for this example app. After creating a new project, create the following feature flags with Client-side SDK availability:
17+
18+
- `bootstrap-flags` - (Boolean) - This flag will determine whether or not the LaunchDarkly React SDK will bootstrap feature flags from the edge.
19+
- `show-debugging-info` - (Boolean) - This flag is used to expose the current flag values.
20+
- `hero-text` - (String) - This flag is used to dynamically change the hero text. You can make the variations anything you want, e.g. "The best way to buy the products you love."
21+
- `enable-hot-dog-favicon` - (Boolean) - This flag is used in middleware.ts to dynamically load a different favicon.
22+
- `store-closed` - (Boolean) - This flag is evaluated in `middleware.ts` and can be used to load a different home page when the store is closed.
23+
24+
#### Set up the LaunchDarkly Vercel integration
25+
26+
You will need to have the LaunchDarkly Vercel integration configured to push feature flag data to your Vercel Edge Config. Read the [Vercel documentation](https://docs.launchdarkly.com/integrations/vercel/) to set up the integration. Be sure to connect the project you created above.
27+
28+
#### Set up environment variables
29+
30+
1. Copy this directory in a new repository.
31+
2. Create a new Vercel project based on the new repository.
32+
3. [Add a new environment variable to your project](https://vercel.com/docs/concepts/projects/environment-variables) named `LD_CLIENT_SIDE_ID` and set it to the LaunchDarkly client-side ID for the **Test** environment in the project you created above.
33+
4. Follow [Vercel's documentation](https://vercel.com/docs/storage/edge-config/get-started) to connect an Edge Config to your new project.
34+
5. Run the following command to link your local codebase to your Vercel project:
35+
36+
```shell
37+
vercel link
38+
```
39+
40+
6. Run the following command to sync your projects environment variables in your development environment:
41+
42+
```shell
43+
vercel env pull .env.development.local
44+
```
45+
46+
7. After completing the guide above, you should have linked this example app to your Vercel project and created an `.env.development.local`.
47+
8. Verify the contents of `.env.development.local` have values for the `LD_CLIENT_SIDE_ID` and `EDGE_CONFIG`.
48+
9. Run the following command to install all dependencies:
49+
50+
```shell
51+
yarn
52+
```
53+
54+
10. Run the following command to start your development environment:
55+
56+
```shell
57+
yarn dev
58+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export default function Closed() {
2+
return (
3+
<div className="flex flex-col items-center min-h-[calc(100vh-44px)] justify-center bg-gray-100">
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
preserveAspectRatio="xMidYMid"
7+
viewBox="0 0 256 315"
8+
className="w-16 h-16 text-black"
9+
>
10+
<path
11+
fill="currentColor"
12+
d="M213.803 167.03c.442 47.58 41.74 63.413 42.197 63.615-.35 1.116-6.599 22.563-21.757 44.716-13.104 19.153-26.705 38.235-48.13 38.63-21.05.388-27.82-12.483-51.888-12.483-24.061 0-31.582 12.088-51.51 12.871-20.68.783-36.428-20.71-49.64-39.793-27-39.033-47.633-110.3-19.928-158.406 13.763-23.89 38.36-39.017 65.056-39.405 20.307-.387 39.475 13.662 51.889 13.662 12.406 0 35.699-16.895 60.186-14.414 10.25.427 39.026 4.14 57.503 31.186-1.49.923-34.335 20.044-33.978 59.822M174.24 50.199c10.98-13.29 18.369-31.79 16.353-50.199-15.826.636-34.962 10.546-46.314 23.828-10.173 11.763-19.082 30.589-16.678 48.633 17.64 1.365 35.66-8.964 46.64-22.262"
13+
/>
14+
</svg>
15+
<h1 className="text-5xl tracking-tight max-w-3xl font-semibold mb-4 mt-10">
16+
We&apos;ll be back.
17+
</h1>
18+
<p className="ml-4 text-gray-500 text-xl">
19+
We&apos;re busy updating the Apple Store for you and will be back soon.
20+
</p>
21+
</div>
22+
);
23+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import 'tailwindcss/tailwind.css';
2+
import Nav from 'components/nav';
3+
import { ReactElement } from 'react';
4+
import { headers } from 'next/headers';
5+
import { LDMultiKindContext } from '@launchdarkly/vercel-server-sdk';
6+
import LaunchDarklyProvider from 'components/launchdarklyProvider';
7+
import { ldEdgeClient } from 'lib/ldEdgeClient';
8+
9+
// Specify the `edge` runtime to use the LaunchDarkly Edge SDK in layouts
10+
export const runtime = 'edge';
11+
12+
export default async function RootLayout({ children }: { children: ReactElement }) {
13+
const headersList = headers();
14+
await ldEdgeClient.waitForInitialization();
15+
16+
// Here we are using basic information from the request as the LaunchDarkly context. If you have session auth in place,
17+
// you will likely want to also include user and organization context.
18+
const context: LDMultiKindContext = {
19+
kind: 'multi',
20+
user: { key: 'anonymous', anonymous: true },
21+
'user-agent': { key: headersList.get('user-agent') || 'unknown' },
22+
method: {
23+
key: 'GET',
24+
},
25+
};
26+
27+
// The allFlagsState call is used to evaluate all feature flags for a given context so they can be bootstrapped but the
28+
// LaunchDarkly React SDK in the `<LaunchDarklyProvider>` component.
29+
const allFlags = (await ldEdgeClient.allFlagsState(context)).toJSON() as {
30+
'bootstrap-flags': boolean;
31+
};
32+
const bootstrappedFlags = allFlags['bootstrap-flags'] ? allFlags : undefined;
33+
34+
return (
35+
<html lang="en">
36+
<body>
37+
<LaunchDarklyProvider
38+
envId={process.env.LD_CLIENT_SIDE_ID || ''}
39+
context={context}
40+
bootstrappedFlags={bootstrappedFlags}
41+
>
42+
<Nav />
43+
{children}
44+
</LaunchDarklyProvider>
45+
</body>
46+
</html>
47+
);
48+
}

0 commit comments

Comments
 (0)