Skip to content

Getting started

This guide adds Feature Flags to a backend app, declares a few common flag types, evaluates a flag from a route, and uses request context for targeting.

Import featureFlags and flag, create a product instance, and register it with your app.

Terminal window
import { backend } from "@layeron/core"
import { featureFlags, flag } from "@layeron/modules"
const app = backend({ project: "shop" })
const flags = featureFlags({
name: "main",
namespace: "product",
flags: {
checkoutV2: flag.boolean({
default: false,
environments: {
preview: true,
staging: true,
prod: false,
},
}),
pricingPageVariant: flag.variant({
default: "control",
variants: ["control", "short-copy", "new-layout"],
}),
maxUploadMb: flag.number({
default: 25,
environments: {
prod: 50,
},
}),
},
})
app.use(flags)

The Feature Flags instance identity is product/main. See Namespaces for platform namespace defaults and naming rules.

Use flags.enabled(...) when the result should be a boolean:

Terminal window
app.get("/checkout", async (request) => {
const enabled = await flags.enabled("checkoutV2", {
environment: "prod",
})
return Response.json({
checkoutVersion: enabled ? "v2" : "v1",
})
})

Use flags.value<T>(...) for strings, numbers, variants, and JSON values:

Terminal window
app.get("/upload-limit", async (request) => {
const maxUploadMb = await flags.value<number>("maxUploadMb", {
environment: "prod",
default: 25,
})
return Response.json({ maxUploadMb })
})

Add a tenant rule when a customer should receive a value before the rest of the audience:

Terminal window
const flags = featureFlags({
name: "main",
flags: {
checkoutV2: flag.boolean({
default: false,
rules: [
flag.tenants(["tenant_acme"]).value(true),
],
}),
},
})

Pass the active tenant ID during evaluation:

Terminal window
const enabled = await flags.enabled("checkoutV2", {
tenantId: tenant.id,
})

Attributes let you use information from your own app model:

Terminal window
const flags = featureFlags({
name: "main",
flags: {
advancedReports: flag.boolean({
default: false,
rules: [
flag.attribute("plan").equals("enterprise").value(true),
],
}),
},
})
Terminal window
const enabled = await flags.enabled("advancedReports", {
tenantId: tenant.id,
attributes: {
plan: tenant.plan,
},
})

Use percentage rules for gradual launches:

Terminal window
const flags = featureFlags({
name: "main",
flags: {
checkoutV2: flag.boolean({
default: false,
rules: [
flag.percentage(10, { stickiness: "userId" }).value(true),
],
}),
},
})

With stickiness: "userId", the same user keeps the same decision as the percentage changes.

Terminal window
const enabled = await flags.enabled("checkoutV2", {
userId: user.id,
})

Use evaluate(...) when you want the selected value and the reason:

Terminal window
const decision = await flags.evaluate<boolean>("checkoutV2", {
environment: "prod",
tenantId: tenant.id,
userId: user.id,
})
console.log(decision.reason, decision.ruleId, decision.value)

The decision reason is one of:

  • environment
  • tenant
  • user
  • attribute
  • percentage
  • default
  • fallback
  • Rules and rollouts: Target tenants, users, attributes, and percentages in a predictable order.
  • Environments: Use environment overrides and promotion habits across preview, staging, and production.
  • Policy: Protect reads, publishes, history, rollback, and admin workflows.
  • Publishing: Publish snapshots, inspect decisions, roll back changes, and audit flag updates.
  • Examples: Adapt release, rollout, and runtime-limit patterns.
  • API reference: Review flag config, decision results, history, rollback, and module methods.