Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/**'],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't figure out how to get eslint to play nicely with the eslintrc file in the complete example so I decided to just ignore it here.

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