Client-Side Feature Flags with OFREP¶
Logfire's managed variables can serve as feature flags for client-side applications like web frontends, mobile apps, and edge services. The OFREP (OpenFeature Remote Evaluation Protocol) endpoints let any OpenFeature-compatible client evaluate variables without the Python SDK.
This guide shows how to set up a JavaScript/TypeScript web application using the official OpenFeature Web SDK and OFREP provider. The same approach works for any language with an OpenFeature SDK and OFREP provider.
Prerequisites¶
- Create your variables in the Logfire UI (Settings > Variables) and mark them as external — see External Variables and OFREP
- Create an API key with the
project:read_external_variablesscope — this restricted scope is safe to use in client-side code since it only exposes variables you've explicitly marked as external
Installation¶
Install the OpenFeature Web SDK and OFREP provider:
npm install @openfeature/web-sdk @openfeature/ofrep-web-provider
pnpm add @openfeature/web-sdk @openfeature/ofrep-web-provider
yarn add @openfeature/web-sdk @openfeature/ofrep-web-provider
Setup¶
Initialize the OpenFeature provider once at application startup. The OFREP provider connects to your Logfire project's OFREP endpoint and handles authentication via your API key.
import { OFREPWebProvider } from '@openfeature/ofrep-web-provider'
import { OpenFeature } from '@openfeature/web-sdk'
const LOGFIRE_API_KEY = 'your-api-key' // project:read_external_variables scope
const LOGFIRE_API_HOST = 'logfire-api.pydantic.dev' // or your self-hosted API host
const provider = new OFREPWebProvider({
baseUrl: `https://${LOGFIRE_API_HOST}/v1/ofrep/v1`,
fetchImplementation: (input, init) =>
fetch(input, {
...init,
headers: {
...Object.fromEntries(new Headers(init?.headers).entries()),
Authorization: `Bearer ${LOGFIRE_API_KEY}`,
},
}),
})
OpenFeature.setProvider(provider)
API key in client-side code
The project:read_external_variables scope is designed to be safe for client-side use. It only grants read access to variables you've explicitly marked as external. Keep sensitive configuration in internal (non-external) variables, which are inaccessible with this scope.
Setting Evaluation Context¶
Set the evaluation context to enable targeting and deterministic rollouts. The targetingKey ensures that the same user always gets the same variant:
await OpenFeature.setContext({
targetingKey: userId,
// Additional attributes for targeting rules
plan: 'enterprise',
region: 'us-east',
})
Any attributes you include in the context can be used by override rules configured in the Logfire UI. For example, you could route all enterprise plan users to a specific label.
Evaluating Flags¶
Use the OpenFeature client to evaluate flags. The client provides typed methods for different value types:
const client = OpenFeature.getClient()
// Boolean flag
const showNewFeature = client.getBooleanValue('show_new_feature', false)
// String value (e.g., a theme or prompt)
const theme = client.getStringValue('ui_theme', 'light')
// Number value
const maxRetries = client.getNumberValue('max_retries', 3)
// Get detailed evaluation info
const details = client.getStringDetails('ui_theme', 'light')
console.log(details.value) // resolved value
console.log(details.variant) // label name (e.g., "production", "canary")
console.log(details.reason) // evaluation reason (e.g., "TARGETING_MATCH", "SPLIT")
The second argument to each method is the default value, returned when the flag can't be evaluated (e.g., network error, flag not found).
React Integration¶
For React applications, OpenFeature provides a React SDK with hooks for flag evaluation:
npm install @openfeature/react-sdk
pnpm add @openfeature/react-sdk
yarn add @openfeature/react-sdk
Wrap your application with the OpenFeatureProvider and use hooks in your components:
import { OpenFeatureProvider, useBooleanFlagValue, useStringFlagDetails } from '@openfeature/react-sdk'
// In your app root
function App() {
return (
<OpenFeatureProvider>
<MyComponent />
</OpenFeatureProvider>
)
}
// In any component
function MyComponent() {
const showBanner = useBooleanFlagValue('show_banner', false)
const theme = useStringFlagDetails('ui_theme', 'light')
return (
<div data-theme={theme.value}>
{showBanner && <PromoBanner />}
<p>Theme variant: {theme.variant}</p>
</div>
)
}
Full Example¶
Here's a complete setup combining initialization, context, and evaluation:
import { OFREPWebProvider } from '@openfeature/ofrep-web-provider'
import { OpenFeature } from '@openfeature/web-sdk'
// Initialize once at app startup
function initFeatureFlags(apiKey: string, apiHost: string) {
const provider = new OFREPWebProvider({
baseUrl: `https://${apiHost}/v1/ofrep/v1`,
fetchImplementation: (input, init) =>
fetch(input, {
...init,
headers: {
...Object.fromEntries(new Headers(init?.headers).entries()),
Authorization: `Bearer ${apiKey}`,
},
}),
})
OpenFeature.setProvider(provider)
}
// Set context when user authenticates
async function setUserContext(userId: string, attributes: Record<string, string> = {}) {
await OpenFeature.setContext({
targetingKey: userId,
...attributes,
})
}
// Evaluate flags anywhere in your app
function getFeatureFlags() {
const client = OpenFeature.getClient()
return {
showNewDashboard: client.getBooleanValue('show_new_dashboard', false),
pricingTier: client.getStringValue('pricing_tier_config', 'standard'),
maxUploadSize: client.getNumberValue('max_upload_size_mb', 10),
}
}
Other Languages and Platforms¶
OpenFeature provides SDKs and OFREP providers for many languages. You can use the same Logfire OFREP endpoint with any of them:
| Platform | SDK | OFREP Provider |
|---|---|---|
| JavaScript (Web) | @openfeature/web-sdk |
@openfeature/ofrep-web-provider |
| JavaScript (Server) | @openfeature/server-sdk |
@openfeature/ofrep-provider |
| Kotlin / Android | OpenFeature Kotlin SDK | OFREP Provider |
| Swift / iOS | OpenFeature Swift SDK | OFREP Provider |
See the OpenFeature ecosystem page for a full list.
The OFREP endpoint format is the same regardless of client:
POST https://<your-api-host>/v1/ofrep/v1/evaluate/flags/{flag_key}
POST https://<your-api-host>/v1/ofrep/v1/evaluate/flags
Both endpoints accept a JSON body with a context object containing targetingKey and any additional targeting attributes:
{
"context": {
"targetingKey": "user-123",
"plan": "enterprise",
"region": "us-east"
}
}
Authenticate with an Authorization: Bearer <api-key> header using a key with project:read_external_variables scope.