Next.js - Weather Search App

This is an example app demonstrating how to use Schematic in a Next.js app with Clerk authentication. This app uses schematic-react for usage tracking and entitlement management via feature flags, and uses schematic-components for an embedded customer portal experience. The Github repo is here and can be cloned to run locally.

Prerequisites

In order to use this example app, you’ll need:

  1. A Schematic account
  2. A Clerk account

In order to make full use of the capabilities of Schematic components, you’ll also need:

  1. A Stripe account
  2. Stripe customer IDs stored in private metadata on your Clerk companies

Getting Started

  1. Set up your Schematic account; add features for “Weather Search”, “Humidity”, and “Wind Speed”, create some plans and entitlements for these features, connect your Stripe account, and connect your Clerk account.

  2. Set up your .env file:

$cp .env.example .env
  1. In the Schematic app, create a component and store its component ID in your .env file:
$NEXT_PUBLIC_SCHEMATIC_COMPONENT_ID="your-component-id"
  1. In the Schematic app, create a new API key and store both the publishable key and secret in your .env file:
$NEXT_PUBLIC_SCHEMATIC_PUBLISHABLE_KEY="api_"
>SCHEMATIC_SECRET_KEY="sch_dev_"
  1. Store your Clerk secret and publishable keys in your .env file:
$CLERK_SECRET_KEY="sk_test_"
>NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_"
  1. Install dependencies:
$npm install
># or
>yarn
># or
>pnpm install
  1. Run the development server:
$npm run dev
># or
>yarn dev
># or
>pnpm dev
># or
>bun dev
  1. Open http://localhost:3000 with your browser to see the result.

How it Works

  1. First, we wrap our client-side code with the <SchematicProvider> provider:
1<SchematicProvider publishableKey={schematicPubKey}></SchematicProvider>
  1. Then, we set a user context with the identify function from the useSchematicEvents hook; this is called in the ClientWrapper component, which wraps all client-side code.
1const { identify } = useSchematicEvents();
2const authContext = useAuthContext();
3
4useEffect(() => {
5 const { company, user } = authContext ?? {};
6 if (company && user) {
7 void identify({
8 company: {
9 keys: company.keys,
10 name: company.name,
11 },
12 keys: user.keys,
13 name: user.name,
14 traits: user.traits,
15 });
16 }
17}, [authContext, identify]);
  1. When we complete a weather search, we log that usage to Schematic using the track function from the useSchematicEvents hook:
1track({ event: "weather-search" });
  1. To enforce entitlements, we use the useSchematicFlag hook in various locations:
1const humidityFlag = useSchematicFlag("humidity");
2
3{
4 humidityFlag && <p>Humidity: {weatherData?.humidity}%</p>;
5}
  1. To display our customer portal, we use the SchematicEmbed component:
1<SchematicEmbed accessToken={accessToken} id={componentId} />
  1. To provide a temporary access token for our embedded component, we implement a backend route to exchange a secret access token for a temporary, company-scoped access token:
1const schematicClient = new SchematicClient({ apiKey });
2const resp = await schematicClient.accesstokens.issueTemporaryAccessToken({
3 resourceType: "company",
4 lookup: {
5 clerkId: orgId,
6 },
7});
8const accessToken = resp.data?.token;
9return NextResponse.json({ accessToken });