Backstage
Backstage is a headless environment & feature flag library that isn't tied to any specific service or format. It's a React based library that focuses on being easy to use and simple to integrate with any (and multiple) feature providers.
The current available providers are:
-
@softwareimaging/backstage-local
- A provider for a local JSON file as configuration. -
@softwareimaging/backstage-azure-config
- A provider to use the Azure App Configuration flag backend. -
[TODO]
-@softwareimaging/backstage-cra
-create-react-app
environment variables. -
[TODO]
-@softwareimaging/backstage-http
- A provider for using a simple, single endpoint with optional headers.
Usage
Install the main library first.
npm i @softwareimaging/backstage --save
And then any providers you want to use, e.g.
npm i @softwareimaging/backstage-local @softwareimaging/backstage-azure-config --save
See the @softwareimaging/backstage-local documentation on how to set up a local environment file.
Adopt and configure the provider as close to the top of component tree as possible.
// backstage-provider.jsx
import { Backstage } from '@softwareimaging/backstage'
import LocalProvider from '@softwareimaging/backstage-local'
import config from './backstage.local'
const BackstageProvider = ({ children }) => {
const providers = [LocalProvider(1, { config })]
return <Backstage providers={providers}>{children}</Backstage>
}
Backstage uses suspense to manage it's loading state. Provide a suspense boundary and a fallback state. See the React documentation for more information.
import { Suspense } from 'react'
import { BackstageProvider } from './backstage-provider'
const App = () => {
return (
<Suspense fallback="Loading...">
<BackstageProvider>
<RestOfApplication />
</BackstageProvider>
</Suspense>
)
}
To use it in your components, use the useVariable
and useFlag
hooks.
import { useVariable, useFlag } from '@softwareimaging/backstage'
const Component = () => {
const name = useVariable('name') // string
const showBanner = useFlag('banner') // boolean
return <div>{showBanner && <Banner>Hello {name}!</Banner>}</div>
}
const Component = () => {
const name = useVariable('name') // string
return (
<div>
<Flag name="banner">
<Banner>Hello {name}!</Banner>
</Flag>
</div>
)
}
Use with TypeScript
It's recommend for TypeScript projects to use an interface to define the known options for your configuration for all of the type-safe-goodness that comes with TypeScript.
Firstly, define your config interface.
// backstage-config.ts
export interface MyConfig extends BackstageConfig {
variables: {
apiKey: string
name: string
}
flags: {
banner: boolean
promotion: boolean
}
}
export type Variables = keyof MyConfig['variables']
export type Flags = keyof MyConfig['flags']
It's also worth defining an extra export of your possible variable & flag names. This will make typing your hook calls have less boilerplate.
While that may look a bit weird, it is simply just a type represents the keys in the object. In the example above Variables
is equal to 'apiKey' | 'name'
and Flags
is equal to 'banner' | 'promotion'
.
To use this in your components, add the type to your hook calls.
import { useFlag } from '@softwareimaging/backstage'
import { Flags } from './backstage-config'
const Text = () => {
const promotion = useFlag<Flags>('promotion') // <- This key is typechecked.
return promotion ? <p>Discount: £10!</p> : <p>Normal pricing: £100</p>
}
Provider Development
If you want to implement a new provider, please use the example and types below as a starting point.
// Please note the nested function, this allows for custom
// priorities at run time. Feel free to use additional arguments
// for configuration.
const provider: BackstageProvider = (priority: number = 0) => () => {
return {
priority,
name: 'Example Provider',
get: async () => {
return {
variables: {
name: 'Website name',
colour: '#29008a'
},
flags: {
banner: true
}
}
}
}
}