diff --git a/.eslintrc.js b/.eslintrc.js index 38837c520..6dd1a6cc9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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', diff --git a/package.json b/package.json index 440397482..419294818 100644 --- a/package.json +++ b/package.json @@ -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, diff --git a/packages/sdk/vercel/README.md b/packages/sdk/vercel/README.md index fa0785f37..a2ca6fbf7 100644 --- a/packages/sdk/vercel/README.md +++ b/packages/sdk/vercel/README.md @@ -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 @@ -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 = { @@ -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 diff --git a/packages/sdk/vercel/examples/README.md b/packages/sdk/vercel/examples/README.md new file mode 100644 index 000000000..6bef05a4a --- /dev/null +++ b/packages/sdk/vercel/examples/README.md @@ -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). diff --git a/packages/sdk/vercel/examples/complete/.eslintrc.json b/packages/sdk/vercel/examples/complete/.eslintrc.json new file mode 100644 index 000000000..a2569c2c7 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": "next/core-web-vitals" +} diff --git a/packages/sdk/vercel/examples/complete/.gitignore b/packages/sdk/vercel/examples/complete/.gitignore new file mode 100644 index 000000000..34d63f073 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.gitignore @@ -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 \ No newline at end of file diff --git a/packages/sdk/vercel/examples/complete/.npmrc b/packages/sdk/vercel/examples/complete/.npmrc new file mode 100644 index 000000000..e9ee3cb4d --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true \ No newline at end of file diff --git a/packages/sdk/vercel/examples/complete/README.md b/packages/sdk/vercel/examples/complete/README.md new file mode 100644 index 000000000..06a333c04 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/README.md @@ -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 +``` diff --git a/packages/sdk/vercel/examples/complete/app/closed/page.tsx b/packages/sdk/vercel/examples/complete/app/closed/page.tsx new file mode 100644 index 000000000..d8f6e833a --- /dev/null +++ b/packages/sdk/vercel/examples/complete/app/closed/page.tsx @@ -0,0 +1,23 @@ +export default function Closed() { + return ( +
+ + + +

+ We'll be back. +

+

+ We're busy updating the Apple Store for you and will be back soon. +

+
+ ); +} diff --git a/packages/sdk/vercel/examples/complete/app/layout.tsx b/packages/sdk/vercel/examples/complete/app/layout.tsx new file mode 100644 index 000000000..613257dbf --- /dev/null +++ b/packages/sdk/vercel/examples/complete/app/layout.tsx @@ -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 `` component. + const allFlags = (await ldEdgeClient.allFlagsState(context)).toJSON() as { + 'bootstrap-flags': boolean; + }; + const bootstrappedFlags = allFlags['bootstrap-flags'] ? allFlags : undefined; + + return ( + + + +