0.1.4 • Published 2 years ago

dende v0.1.4

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

Dende - Next Server Toolbox

Overview

Dende is a toolbox for fullstack Next solutions that make it easier to translate your backend to your frontend. Dende includes solutions for feature flagging, and Server Component prop translation. Dende is a typescript package.

Install

Yarn

yarn install dende

pnpm

pnpm add dende


Feature Flags - Setup

Defining Flags - Environment Variables

You can define your feature flags in your app's env files or your app host's environment variable definitions (if applicable). Use a "DENDE" prefix in your environment variable in order for to be properly calculated. For example:

# env.local
DENDE_SECRET_FEATURE=true
#env.staging
DENDE_SECRET_FEATURE=true
#env
DENDE_SECRET_FEATURE=false

In this example, we can use DENDE_SECRET_FEATURE to hide a component from production.

Defining Flags - dende.config.ts

You can define a config file at the root of your project called dende.config.ts. You can use it to define feature flag states using the following strategies:

Static Flags

Similar to environment variables, you can also create a static flags using a custom feature state map with dende.config.ts file. For example:

// dende.config.ts

import { createFeatureConfig } from "dende/config";

export default createFeatureConfig({
  secretFeature: false,
  publicFeature: true,
});

Async Flags - dende.config.ts

If you want more dynamic feature flags or you want to retrieve them from a 3rd party vendor, you can use our cachedResolver in dende.config.ts file. Our cachedResolver utilizes Next fetch to call user defined API's and cache the responses. Here's how to use it:

// dende.config.ts

import { createFeatureConfig, cachedResolver } from "dende/config";

export default createFeatureConfig({
  asyncSecretFeature: () => cachedResolver("/api/hello", { callback: () => true }),
  asyncPublicFeature: () => cachedResolver("/api/hello",
    { callback: () => true, tags: ['holidayFeature'] }),
});

Busting Resolver Cache

Since our cachedResolver use Next fetch, you're able to take control of the cache management. In the second paramater, tags are allowed to be used (part of the Next fetch API), which enable you to bust the cache for groups of feature states as needed. Here's how you can do this with Next API routes:

Create a route dedicated to clear the cache for a set up features using a certain tag:

// app/api/features/revalidate/route.ts

import { updateFeature } from 'dende/config';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const { searchParams } = new URL(request.url);
  const secret = searchParams.get('secret');
  const tag = searchParams.get('tag');

  // Define an env var secret to protect this route from the public
  if (secret !== process.env.FEATURES_SECRET) {
    return NextResponse.error();
  }

  updateFeature(tag);

  return NextResponse.json({}, { status: 200 });
}

A call to /api/features/revalidate?tag=holidayFeature will now bust the cached response made for calls the API endpoints defined in the cachedResolver URL param. So the subsequent call to that URL will request, receive and cache the latest feature state data from the third party.

Client Setup - with Next Server Components and React Context

In an async page or layout component, use calculateFeatures from dende/config to calculate the feature flag states.

Layout component (Recommended)

Calculating feature states makes the most sense at the layout level because feature states are typically meant to be global. Here's how it works with layout:

layout.tsx

import { PropsWithChildren } from 'react';
import { calculateFeatures } from 'dende/config';

export default async function Layout({ children }: PropsWithChildren) {
  const features = await calculateFeatures();

  return (
    <AppShell features={features}>
      <h1>Hello World</h1>
    </AppShell>
  )
}

AppShell.tsx (example layout wrapper)

'use client'

import { FeatureMap } from 'dende/config';

type Props = PropsWithChildren & { features: FeatureMap }

export default function AppShell({ features, children }: Props) {
  return (
    <FeatureProvider map={features}>
      {chidlren}
    </FeatureProvider>
  )
}

Page component

page.tsx

import { calculateFeatures } from 'dende/config';

export default async function Page() {
  const features = await calculateFeatures();

  return (
    <PageWrapper>
      <h1>Hello World</h1>
    </PageWrapper>
  )
}

PageWrapper.tsx

'use client'

import { FeatureMap } from 'dende/config';

type Props = PropsWithChildren & { features: FeatureMap }

export default function PageWrapper({ features, children }: Props) {
  return (
    <FeatureProvider map={features}>
      {children}
    </FeatureProvider>
  )
}

Components

You can use our FeatureToggle and FeatureSetToggle component to gate certain client components based on feature flag states.

FeatureToggle

  <FeatureToggle feature="publicFeature">
    <div>Hello World</div>
  </FeatureToggle>

The component will show bsaed on the feature flag state.

FeatureSetToggle

  <FeatureSetToggle features={["publicFeature", "secretFeature"]}>
    <div>Hello World</div>
  </FeatureSetToggle>

Hooks

You can use our useFeature and useFeatureSet hooks to gate more intricate component logic based on feature flag state.

useFeature

  const Component = () => {
    const activateFeature = useFeature('publicFeature');
    return (
      <div>
        <h1>Hello World</h1>
        {activateFeature ? (
          <h2>There's a new feature ready!</h2>
        ) : (
          <h2>There's old feature</h2>
        )}
      </div>
    )
  }

useFeatureSet

  const Component = () => {
    const activateFeature = useFeatureSet(['secretFeature', 'publicFeature']);
    return (
      <div>
        <h1>Hello World</h1>
        {activateFeature ? (
          <h2>Secret feature is ready!</h2>
        ) : (
          <h2>Secret feature is not ready yet</h2>
        )}
      </div>
    )
  }
0.1.4

2 years ago

0.1.3

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago