You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FeatureBoard's node/browser SDKs have been designed to work together but have different entrypoints. On the server you need to initialise the server SDK which maintains a full copy of the feature state tree, allowing the features for a request to be calculated without connecting to the FeatureBoard service.
We do this because it means FeatureBoard will not slow down your requests, and FeatureBoard also will not affect your server's reliability if we happen to have an issue.
The server looks something like this:
asyncfunctioncreateServer(){constfeatureBoardConnection=awaitServerConnection.init(...)app.use((req)=>{req.featureboardClient=featureBoardConnection.request(['plan-medium','other-audiences'])// pass this to the <FeatureBoardProvider client={req.featureboardClient}>})}
On the client you can use the useClient hook which takes care of initialising the sdk, and re-initialising it if the users audiences happen to change. The SDK is initialised for a single set of audiences to reduce data transfer and reduce the amount of information sent to the browser.
NextJS doesn't allow you to hook into these two parts of the request pipeline and instead works at a higher abstraction level. This means we need to extend our SDK to open up options to use our SDK with NextJS or make it transparently isomorphic.
Option 1 - Server side integration only
getServerSideProps is an isomorphic method, during a server side render it runs to get the props for _app. Then during a client side navigation nextjs will call a endpoint maintained by next starting with the _next prefix.
This means that if our SDK enforces usage of getServerSideProps the user now has to deal with a API call which MUST be made before all page navigations and locks usage into the Server Side Rendering approach or be forced to use SSG, which isn't great for dynamic sites.
None the less, will document in case I find workarounds for the drawbacks.
importReactfrom'react';import{GetServerSideProps}from'next';importLayoutfrom'components/Layout';import{AppProps}from'next/app';import{createClient}from'@featureboard/js-sdk';import{FeatureBoardService,ServerConnection}from'@featureboard/node-sdk';import{FeatureBoardProvider}from'@featureboard/react-sdk';letserverConnection: ServerConnection;exportconstgetServerSideProps: GetServerSideProps=async({ req })=>{// Calculate the audiences from the requestconstaudiences: string[]=[];if(!serverConnection){if(!process.env.FEATUREBOARD_ENV_KEY){thrownewError('FEATUREBOARD_ENV_KEY missing');}serverConnection=awaitFeatureBoardService.init(process.env.FEATUREBOARD_ENV_KEY,{updateStrategy: 'on-request'});}return{props: {featureState: serverConnection.getEffectiveValues(audiences)}};};exportdefaultfunctionMyApp({ Component, pageProps }: AppProps){return(<divclassName="bg-primary"><FeatureBoardProviderclient={createClient(pageProps.featureState)}><Layout><Component{...pageProps}/></Layout></FeatureBoardProvider></div>);}
To get this approach working, we need to extend our public API to include import { createClient } from '@featureboard/js-sdk', this simply creates a client based on an effective feature set.
Option 2 - Make the JS SDK Isomorphic
This is a more complex option.
Create 2 entrypoints browser / server. The node SDK optimisations would get moved into @featureboard/js-sdk/server as a seprate entrypoint, the exports from @featureboard/js-sdk would behave differently depending on if they are imported from the browser or the server.
The main export would become getClient(['user', 'audiences'])
On the server
When getClientAsync is called, if there is no SDK initialised it will start initialising the SDK. Once the SDK has fully initialised it will resolve the promise.
Multiple calls to getClientAsync will return the same promise instance.
Option 3 - Make the React SDK isomorphic
Create 2 versions of the hook, on the server it stores the reference in a global, on the client in a react ref. Or could simply have 2 entrypoints which import the same hook, but pass the current client down, or have a factory function so the initialisation can be inverted.
Option 4 - Use Document extension + useClient hook
A custom document doesn't have the same downsides as _app, we could wrap the
in the FeatureBoardProvider with the server version of the SDK after it has been initialised.
Then in _app we could use the useClient hook if there isn't already a client created with context. This would be stable from a hooks point of view because the client would never become set allowing us to ignore the rules of hooks for this instance.
Background
FeatureBoard's node/browser SDKs have been designed to work together but have different entrypoints. On the server you need to initialise the server SDK which maintains a full copy of the feature state tree, allowing the features for a request to be calculated without connecting to the FeatureBoard service.
We do this because it means FeatureBoard will not slow down your requests, and FeatureBoard also will not affect your server's reliability if we happen to have an issue.
The server looks something like this:
On the client you can use the
useClient
hook which takes care of initialising the sdk, and re-initialising it if the users audiences happen to change. The SDK is initialised for a single set of audiences to reduce data transfer and reduce the amount of information sent to the browser.NextJS doesn't allow you to hook into these two parts of the request pipeline and instead works at a higher abstraction level. This means we need to extend our SDK to open up options to use our SDK with NextJS or make it transparently isomorphic.
Option 1 - Server side integration only
getServerSideProps
is an isomorphic method, during a server side render it runs to get the props for _app. Then during a client side navigation nextjs will call a endpoint maintained by next starting with the _next prefix.This means that if our SDK enforces usage of
getServerSideProps
the user now has to deal with a API call which MUST be made before all page navigations and locks usage into the Server Side Rendering approach or be forced to use SSG, which isn't great for dynamic sites.None the less, will document in case I find workarounds for the drawbacks.
To get this approach working, we need to extend our public API to include
import { createClient } from '@featureboard/js-sdk'
, this simply creates a client based on an effective feature set.Option 2 - Make the JS SDK Isomorphic
This is a more complex option.
Create 2 entrypoints browser / server. The node SDK optimisations would get moved into
@featureboard/js-sdk/server
as a seprate entrypoint, the exports from@featureboard/js-sdk
would behave differently depending on if they are imported from the browser or the server.The main export would become
getClient(['user', 'audiences'])
On the server
When getClientAsync is called, if there is no SDK initialised it will start initialising the SDK. Once the SDK has fully initialised it will resolve the promise.
Multiple calls to getClientAsync will return the same promise instance.
Option 3 - Make the React SDK isomorphic
Create 2 versions of the hook, on the server it stores the reference in a global, on the client in a react ref. Or could simply have 2 entrypoints which import the same hook, but pass the current client down, or have a factory function so the initialisation can be inverted.
Option 4 - Use Document extension + useClient hook
A custom document doesn't have the same downsides as _app, we could wrap the
in the FeatureBoardProvider with the server version of the SDK after it has been initialised.Then in _app we could use the
useClient
hook if there isn't already a client created with context. This would be stable from a hooks point of view because the client would never become set allowing us to ignore the rules of hooks for this instance.References
https://nextjs.org/docs/advanced-features/custom-document
https://nextjs.org/docs/advanced-features/automatic-static-optimization (highlights downsides of particular functions)
vercel/next.js#13910
vercel/next.js#1117
vercel/next.js#2582
The text was updated successfully, but these errors were encountered: