Skip to content

Commit

Permalink
feat: add new Vercel example (#130)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ldhenry authored Jun 2, 2023
1 parent 2296c4f commit d25f327
Show file tree
Hide file tree
Showing 44 changed files with 661 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
project: './tsconfig.eslint.json',
},
plugins: ['@typescript-eslint', 'prettier'],
ignorePatterns: ['**/dist/**'],
ignorePatterns: ['**/dist/**', '**/vercel/examples/**'],
rules: {
'prettier/prettier': ['error'],
'class-methods-use-this': 'off',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"packages/sdk/cloudflare",
"packages/sdk/cloudflare/example",
"packages/sdk/vercel",
"packages/sdk/vercel/example-vercel",
"packages/sdk/vercel/examples/complete",
"packages/sdk/vercel/examples/route-handler",
"packages/sdk/akamai"
],
"private": true,
Expand Down
18 changes: 11 additions & 7 deletions packages/sdk/vercel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
[![NPM][sdk-vercel-dm-badge]][sdk-vercel-npm-link]
[![NPM][sdk-vercel-dt-badge]][sdk-vercel-npm-link]

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).
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).

For more information, see the [SDK features guide](https://docs.launchdarkly.com/sdk/features/storing-data).
## The LaunchDarkly Vercel integration is required

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/).

For more information, see the [Vercel SDK reference](https://docs.launchdarkly.com/sdk/edge/vercel).

## Install

Expand All @@ -22,16 +26,16 @@ or yarn:
yarn add -D @launchdarkly/vercel-server-sdk
```

## Quickstart
## Quick start

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:
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):

```typescript
import init from '@launchdarkly/vercel-server-sdk';
import { init } from '@launchdarkly/vercel-server-sdk';
import { createClient } from '@vercel/edge-config';

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

await ldClient.waitForInitialization();
const ldContext = {
Expand All @@ -42,7 +46,7 @@ const ldContext = {
const flagValue = await ldClient.variation('my-flag', ldContext, true);
```

To learn more, head straight to the [complete reference guide for this SDK](https://docs.launchdarkly.com/sdk/server-side/vercel).
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).

## Developing this SDK

Expand Down
19 changes: 19 additions & 0 deletions packages/sdk/vercel/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# LaunchDarkly Vercel Edge SDK examples

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.

## [Complete example](complete/README.md)

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:

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).
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).

### Demo

https://hello-vercel-edge.vercel.app/

## [Route Handler example](route-handler/README.md)

This is an example test app to showcase the usage of the Vercel LaunchDarkly
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).
4 changes: 4 additions & 0 deletions packages/sdk/vercel/examples/complete/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"root": true,
"extends": "next/core-web-vitals"
}
43 changes: 43 additions & 0 deletions packages/sdk/vercel/examples/complete/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
/node_modules
/.pnp
.pnp.js

# Testing
/coverage

# Next.js
/.next/
/out/

# VS code
/.vscode

# Production
/build

# Misc
.DS_Store
*.pem

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Local ENV files
.env.local
.env.development.local
.env.test.local
.env.production.local

# Vercel
.vercel

# Turborepo
.turbo

# typescript
*.tsbuildinfo
1 change: 1 addition & 0 deletions packages/sdk/vercel/examples/complete/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
legacy-peer-deps=true
58 changes: 58 additions & 0 deletions packages/sdk/vercel/examples/complete/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Complete example app for Vercel LaunchDarkly SDK

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:

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).
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).

## Demo

https://hello-vercel-edge.vercel.app/

## Local development

#### Create a new LaunchDarkly project and flags

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:

- `bootstrap-flags` - (Boolean) - This flag will determine whether or not the LaunchDarkly React SDK will bootstrap feature flags from the edge.
- `show-debugging-info` - (Boolean) - This flag is used to expose the current flag values.
- `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."
- `enable-hot-dog-favicon` - (Boolean) - This flag is used in middleware.ts to dynamically load a different favicon.
- `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.

#### Set up the LaunchDarkly Vercel integration

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.

#### Set up environment variables

1. Copy this directory in a new repository.
2. Create a new Vercel project based on the new repository.
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.
4. Follow [Vercel's documentation](https://vercel.com/docs/storage/edge-config/get-started) to connect an Edge Config to your new project.
5. Run the following command to link your local codebase to your Vercel project:

```shell
vercel link
```

6. Run the following command to sync your projects environment variables in your development environment:

```shell
vercel env pull .env.development.local
```

7. After completing the guide above, you should have linked this example app to your Vercel project and created an `.env.development.local`.
8. Verify the contents of `.env.development.local` have values for the `LD_CLIENT_SIDE_ID` and `EDGE_CONFIG`.
9. Run the following command to install all dependencies:

```shell
yarn
```

10. Run the following command to start your development environment:

```shell
yarn dev
```
23 changes: 23 additions & 0 deletions packages/sdk/vercel/examples/complete/app/closed/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export default function Closed() {
return (
<div className="flex flex-col items-center min-h-[calc(100vh-44px)] justify-center bg-gray-100">
<svg
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
viewBox="0 0 256 315"
className="w-16 h-16 text-black"
>
<path
fill="currentColor"
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"
/>
</svg>
<h1 className="text-5xl tracking-tight max-w-3xl font-semibold mb-4 mt-10">
We&apos;ll be back.
</h1>
<p className="ml-4 text-gray-500 text-xl">
We&apos;re busy updating the Apple Store for you and will be back soon.
</p>
</div>
);
}
48 changes: 48 additions & 0 deletions packages/sdk/vercel/examples/complete/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'tailwindcss/tailwind.css';
import Nav from 'components/nav';
import { ReactElement } from 'react';
import { headers } from 'next/headers';
import { LDMultiKindContext } from '@launchdarkly/vercel-server-sdk';
import LaunchDarklyProvider from 'components/launchdarklyProvider';
import { ldEdgeClient } from 'lib/ldEdgeClient';

// Specify the `edge` runtime to use the LaunchDarkly Edge SDK in layouts
export const runtime = 'edge';

export default async function RootLayout({ children }: { children: ReactElement }) {
const headersList = headers();
await ldEdgeClient.waitForInitialization();

// Here we are using basic information from the request as the LaunchDarkly context. If you have session auth in place,
// you will likely want to also include user and organization context.
const context: LDMultiKindContext = {
kind: 'multi',
user: { key: 'anonymous', anonymous: true },
'user-agent': { key: headersList.get('user-agent') || 'unknown' },
method: {
key: 'GET',
},
};

// The allFlagsState call is used to evaluate all feature flags for a given context so they can be bootstrapped but the
// LaunchDarkly React SDK in the `<LaunchDarklyProvider>` component.
const allFlags = (await ldEdgeClient.allFlagsState(context)).toJSON() as {
'bootstrap-flags': boolean;
};
const bootstrappedFlags = allFlags['bootstrap-flags'] ? allFlags : undefined;

return (
<html lang="en">
<body>
<LaunchDarklyProvider
envId={process.env.LD_CLIENT_SIDE_ID || ''}
context={context}
bootstrappedFlags={bootstrappedFlags}
>
<Nav />
{children}
</LaunchDarklyProvider>
</body>
</html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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">
{/* Heroicon name: outline/exclamation-triangle */}
<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>
</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">
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-apple-store/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>
);
}
Loading

0 comments on commit d25f327

Please sign in to comment.