Flagsmith
"Flagsmith: open-source feature flags, remote config, segments, environments, audit logs, self-hosted, REST API"
Flagsmith is an open-source feature flag and remote configuration service that can run fully self-hosted or as a managed cloud offering. It gives teams complete control over their feature flag infrastructure with no vendor lock-in. The platform separates concerns into flags (boolean toggles), remote config (key-value pairs attached to flags), segments (user groupings based on traits), and environments (dev/staging/production isolation). Every flag change is audit-logged, and the REST API is a first-class citizen — the dashboard is simply a frontend for the same API your code uses. ## Key Points 1. **Enable local evaluation on the server.** This eliminates per-request latency to the Flagsmith API. The SDK polls for environment changes in the background and evaluates flags locally. 2. **Use environments to mirror your deployment pipeline.** Create dev, staging, and production environments. Each has its own API key and independent flag states. 3. **Store complex configuration as JSON strings in flag values.** Flags are not just booleans — attach a JSON value to parameterize behavior without creating multiple flags. 4. **Use the REST API in CI/CD pipelines.** Automate flag promotion across environments as part of your deployment process rather than manually toggling flags in the dashboard. 5. **Set traits on identity to drive segment membership.** Traits are the input; segments are the rules. Keep traits factual (plan, region, signup date) and let segments encode the business logic. 6. **Review audit logs regularly.** Self-hosted Flagsmith gives you full audit trail access. Integrate audit log queries into your observability stack for change correlation. 7. **Use default flag handlers.** Configure a `defaultFlagHandler` so your application degrades gracefully when a flag is not defined rather than throwing errors. 8. **Pin your self-hosted version.** Flagsmith releases frequently. Pin to a specific Docker tag and upgrade deliberately, testing flag evaluation consistency before and after. 1. **Calling `getIdentityFlags` on every request without local evaluation.** This makes a network call each time. Enable local evaluation or cache identity flags with a short TTL. 2. **Using remote config values without parsing.** Flag values come back as strings. Always parse and validate them — do not assume the value is well-formed JSON or a valid number. 3. **Creating environments for feature branches.** Environments are heavyweight and meant for deployment stages. Use segments or traits to separate branch-specific behavior. 4. **Ignoring the environment document endpoint.** For performance-critical paths, fetch the full environment document once and evaluate locally rather than making per-flag API calls.
skilldb get feature-flags-services-skills/FlagsmithFull skill: 371 linesFlagsmith
Core Philosophy
Flagsmith is an open-source feature flag and remote configuration service that can run fully self-hosted or as a managed cloud offering. It gives teams complete control over their feature flag infrastructure with no vendor lock-in. The platform separates concerns into flags (boolean toggles), remote config (key-value pairs attached to flags), segments (user groupings based on traits), and environments (dev/staging/production isolation). Every flag change is audit-logged, and the REST API is a first-class citizen — the dashboard is simply a frontend for the same API your code uses.
Setup
Node.js Server SDK
import Flagsmith from "flagsmith-nodejs";
const flagsmith = new Flagsmith({
environmentKey: process.env.FLAGSMITH_SERVER_KEY!,
apiUrl: process.env.FLAGSMITH_API_URL ?? "https://edge.api.flagsmith.com/api/v1/",
enableLocalEvaluation: true,
environmentRefreshIntervalSeconds: 30,
defaultFlagHandler: (flagName: string) => {
// Fallback when a flag is not found
return { enabled: false, value: null, isDefault: true };
},
});
await flagsmith.getEnvironmentFlags();
// Evaluate a flag for an identity
const flags = await flagsmith.getIdentityFlags("user-123", {
plan: "enterprise",
signup_date: "2024-01-15",
company: "acme-corp",
});
const newSearchEnabled = flags.isFeatureEnabled("new_search");
const searchConfig = flags.getFeatureValue("new_search");
// searchConfig might be a JSON string: '{"algorithm":"semantic","maxResults":50}'
React Client SDK
// providers/flagsmith-provider.tsx
"use client";
import { FlagsmithProvider } from "flagsmith/react";
import flagsmith from "flagsmith";
import { useEffect, useState } from "react";
interface Props {
children: React.ReactNode;
identityKey?: string;
traits?: Record<string, string | number | boolean>;
}
export function FeatureFlagProvider({ children, identityKey, traits }: Props) {
const [ready, setReady] = useState(false);
useEffect(() => {
flagsmith
.init({
environmentID: process.env.NEXT_PUBLIC_FLAGSMITH_ENV_ID!,
api: process.env.NEXT_PUBLIC_FLAGSMITH_API_URL,
identity: identityKey,
traits,
cacheFlags: true,
onChange: () => setReady(true),
enableAnalytics: true,
})
.catch(console.error);
}, [identityKey, traits]);
if (!ready) return null;
return <FlagsmithProvider flagsmith={flagsmith}>{children}</FlagsmithProvider>;
}
Self-Hosted Docker Compose
// scripts/verify-flagsmith.ts — health check for self-hosted instance
async function verifyFlagsmithHealth(baseUrl: string): Promise<boolean> {
const healthEndpoint = `${baseUrl}/health`;
const apiEndpoint = `${baseUrl}/api/v1/environment-document/`;
try {
const healthRes = await fetch(healthEndpoint);
if (!healthRes.ok) {
console.error("Flagsmith health check failed:", healthRes.status);
return false;
}
const apiRes = await fetch(apiEndpoint, {
headers: {
"X-Environment-Key": process.env.FLAGSMITH_SERVER_KEY!,
},
});
if (!apiRes.ok) {
console.error("Flagsmith API check failed:", apiRes.status);
return false;
}
const envDoc = await apiRes.json();
console.log(`Flagsmith ready: ${envDoc.feature_states.length} flags loaded`);
return true;
} catch (err) {
console.error("Flagsmith not reachable:", err);
return false;
}
}
Key Techniques
Local Evaluation Mode
// Local evaluation downloads the full environment config
// and evaluates flags in-process — no network call per evaluation
const flagsmith = new Flagsmith({
environmentKey: process.env.FLAGSMITH_SERVER_KEY!,
enableLocalEvaluation: true,
environmentRefreshIntervalSeconds: 15,
});
// After init, evaluations are purely local
async function evaluateForUser(userId: string, traits: Record<string, string>) {
const flags = await flagsmith.getIdentityFlags(userId, traits);
return {
showBetaFeatures: flags.isFeatureEnabled("beta_features"),
apiRateLimit: Number(flags.getFeatureValue("api_rate_limit")) || 1000,
searchAlgorithm: flags.getFeatureValue("search_algorithm") || "keyword",
};
}
Segments and Trait-Based Targeting
// Traits are properties of an identity used for segment matching
// Segments are defined in the Flagsmith dashboard or via API
async function identifyUser(
userId: string,
userAttributes: {
plan: string;
region: string;
createdAt: string;
employeeCount: number;
}
) {
const flags = await flagsmith.getIdentityFlags(userId, {
plan: userAttributes.plan,
region: userAttributes.region,
created_at: userAttributes.createdAt,
employee_count: userAttributes.employeeCount,
});
// Segments in dashboard might define:
// "enterprise_users" -> trait "plan" equals "enterprise"
// "large_orgs" -> trait "employee_count" greater than 100
// "eu_users" -> trait "region" in ["eu-west-1", "eu-central-1"]
return {
showAdvancedAnalytics: flags.isFeatureEnabled("advanced_analytics"),
exportFormat: flags.getFeatureValue("export_format") || "csv",
maxTeamMembers: Number(flags.getFeatureValue("max_team_members")) || 5,
};
}
REST API for Programmatic Flag Management
// Manage flags programmatically via the REST API
// Useful for CI/CD pipelines, scripts, and custom dashboards
const FLAGSMITH_API = process.env.FLAGSMITH_API_URL!;
const FLAGSMITH_TOKEN = process.env.FLAGSMITH_ADMIN_TOKEN!;
interface FlagState {
id: number;
feature: { id: number; name: string };
enabled: boolean;
feature_state_value: string | null;
}
async function listFlags(environmentKey: string): Promise<FlagState[]> {
const res = await fetch(`${FLAGSMITH_API}/environments/${environmentKey}/featurestates/`, {
headers: {
Authorization: `Token ${FLAGSMITH_TOKEN}`,
"Content-Type": "application/json",
},
});
const data = await res.json();
return data.results;
}
async function toggleFlag(
flagStateId: number,
enabled: boolean,
value?: string
): Promise<void> {
await fetch(`${FLAGSMITH_API}/features/featurestates/${flagStateId}/`, {
method: "PATCH",
headers: {
Authorization: `Token ${FLAGSMITH_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
enabled,
...(value !== undefined && { feature_state_value: value }),
}),
});
}
// CI/CD: enable a flag in staging after deploy
async function enableFlagInStaging(flagName: string) {
const flags = await listFlags(process.env.FLAGSMITH_STAGING_ENV_KEY!);
const target = flags.find((f) => f.feature.name === flagName);
if (target) {
await toggleFlag(target.id, true);
console.log(`Enabled "${flagName}" in staging`);
}
}
Multi-Environment Flag Promotion
// Promote flag configuration from staging to production
async function promoteFlag(flagName: string) {
const stagingFlags = await listFlags(process.env.FLAGSMITH_STAGING_ENV_KEY!);
const stagingFlag = stagingFlags.find((f) => f.feature.name === flagName);
if (!stagingFlag) {
throw new Error(`Flag "${flagName}" not found in staging`);
}
const prodFlags = await listFlags(process.env.FLAGSMITH_PROD_ENV_KEY!);
const prodFlag = prodFlags.find((f) => f.feature.name === flagName);
if (!prodFlag) {
throw new Error(`Flag "${flagName}" not found in production`);
}
await toggleFlag(prodFlag.id, stagingFlag.enabled, stagingFlag.feature_state_value ?? undefined);
console.log(
`Promoted "${flagName}" to production: enabled=${stagingFlag.enabled}, value=${stagingFlag.feature_state_value}`
);
}
Audit Log Consumption
// Fetch audit logs for compliance and debugging
interface AuditLogEntry {
id: number;
created_date: string;
log: string;
author: { email: string } | null;
environment: { name: string } | null;
related_object_type: string;
}
async function getRecentAuditLogs(
projectId: string,
limit: number = 50
): Promise<AuditLogEntry[]> {
const res = await fetch(
`${FLAGSMITH_API}/projects/${projectId}/audit/?page_size=${limit}`,
{
headers: {
Authorization: `Token ${FLAGSMITH_TOKEN}`,
},
}
);
const data = await res.json();
return data.results;
}
async function printFlagChangeHistory(projectId: string, flagName: string) {
const logs = await getRecentAuditLogs(projectId, 100);
const flagLogs = logs.filter((entry) => entry.log.includes(flagName));
for (const entry of flagLogs) {
console.log(
`[${entry.created_date}] ${entry.author?.email ?? "system"} — ${entry.log} (${entry.environment?.name ?? "global"})`
);
}
}
React Hooks for Flag Consumption
"use client";
import { useFlags, useFlagsmith } from "flagsmith/react";
function PricingPage() {
const flags = useFlags(["new_pricing_table", "pricing_config"]);
const flagsmith = useFlagsmith();
const showNewPricing = flags.new_pricing_table?.enabled ?? false;
const pricingConfig = JSON.parse(
flags.pricing_config?.value?.toString() || '{"plans":[]}'
);
function handlePlanSelect(planId: string) {
// Track analytics event
flagsmith.setTrait("selected_plan", planId);
}
if (showNewPricing) {
return (
<NewPricingTable
plans={pricingConfig.plans}
onSelect={handlePlanSelect}
/>
);
}
return <LegacyPricingPage />;
}
Best Practices
-
Enable local evaluation on the server. This eliminates per-request latency to the Flagsmith API. The SDK polls for environment changes in the background and evaluates flags locally.
-
Use environments to mirror your deployment pipeline. Create dev, staging, and production environments. Each has its own API key and independent flag states.
-
Store complex configuration as JSON strings in flag values. Flags are not just booleans — attach a JSON value to parameterize behavior without creating multiple flags.
-
Use the REST API in CI/CD pipelines. Automate flag promotion across environments as part of your deployment process rather than manually toggling flags in the dashboard.
-
Set traits on identity to drive segment membership. Traits are the input; segments are the rules. Keep traits factual (plan, region, signup date) and let segments encode the business logic.
-
Review audit logs regularly. Self-hosted Flagsmith gives you full audit trail access. Integrate audit log queries into your observability stack for change correlation.
-
Use default flag handlers. Configure a
defaultFlagHandlerso your application degrades gracefully when a flag is not defined rather than throwing errors. -
Pin your self-hosted version. Flagsmith releases frequently. Pin to a specific Docker tag and upgrade deliberately, testing flag evaluation consistency before and after.
Anti-Patterns
-
Calling
getIdentityFlagson every request without local evaluation. This makes a network call each time. Enable local evaluation or cache identity flags with a short TTL. -
Using remote config values without parsing. Flag values come back as strings. Always parse and validate them — do not assume the value is well-formed JSON or a valid number.
-
Creating environments for feature branches. Environments are heavyweight and meant for deployment stages. Use segments or traits to separate branch-specific behavior.
-
Ignoring the environment document endpoint. For performance-critical paths, fetch the full environment document once and evaluate locally rather than making per-flag API calls.
-
Storing secrets in flag values. Flag values are sent to client SDKs. Never put API keys, tokens, or passwords in remote config values. Use server-side-only flags for sensitive configuration.
-
Skipping health checks on self-hosted instances. If Flagsmith is down and you do not have local evaluation enabled, all flags fall back to defaults. Monitor the
/healthendpoint and alert on failures. -
Mixing identity keys across services. If your API uses user IDs and your frontend uses session IDs as identity keys, the same person gets inconsistent flag values. Standardize on a single identity key format.
Install this skill directly: skilldb add feature-flags-services-skills
Related Skills
ConfigCat
"ConfigCat: feature flags and remote config with percentage rollouts, targeting rules, config-as-code, and cross-platform SDKs"
Flipt
"Flipt: open-source, self-hosted feature flag platform with GitOps support, boolean and multivariate flags, and GRPC/REST APIs"
GrowthBook
"GrowthBook: open-source feature flags, A/B testing, Bayesian statistics, SDK, targeting, webhooks, self-hosted"
LaunchDarkly
"LaunchDarkly: feature flags, targeting rules, segments, experiments, metrics, Node/React SDK, bootstrap, streaming"
Split.io
"Split.io: feature delivery platform with feature flags, targeting, experimentation, traffic allocation, and metrics integration"
Statsig
"Statsig: feature gates, dynamic config, experiments/A/B tests, metrics, layers, Next.js SDK, server/client evaluation"