On this page

Feature flags

Turn features on for some users (a plan, a segment, a percentage) and change values your app reads at runtime, without releasing the app.

Feature flags let you ship code switched off and turn it on later from Studio: for your team first, then for customers on a plan, then for 10% of everyone, then for all. The same flags carry remote config: text, numbers and JSON your app reads at runtime (an upload limit, a banner text, a price, a group of settings), changed without a release.

  • Flags are evaluated by StitchDB for the signed-in user, from the session, so users cannot change their plan or e-mail to get a feature.
  • Rules target the user (user.*) and what the app sends (attr.*: app version, country, platform), directly or through reusable segments.
  • Percentage rollouts keep each user on the same side for as long as the flag exists.
  • Every change is logged with who made it, what changed, and can be undone.

#Turn it on

In Studio open Integrations › Feature flags and click Turn on feature flags (owners and admins). Then New flag: give it a key (what the app asks for, like new-checkout) and a kind:

  • On / off: two values, true and false.
  • Text, Number or JSON: one or more values (variations) to choose between, e.g. "blue" and "green", or 10, 50 and 500.

A new flag is off. While a flag is off everyone gets its off value (false for on/off flags). When it is on, the targeting rules apply.

#Read flags in your app

JavaScript
import { createClient } from 'stitchdb-js'
const stitchdb = createClient(URL, ANON_KEY)

// once at start
await stitchdb.flags.load({ attributes: { app_version: '2.4.1', country: 'DE', platform: 'web' } })

if (stitchdb.flags.get('new-checkout', false)) showNewCheckout()
const limitMb = stitchdb.flags.get('upload-limit-mb', 10)
const theme = stitchdb.flags.get('theme', { accent: 'blue' })

// values changed (a refresh, a sign-in)
stitchdb.flags.on('change', (keys) => render())

get(key, fallback) answers at once: with the value StitchDB gave for this user, or with fallback while it is not known (before the first load, offline on the first start, a deleted or archived flag). The SDK keeps the last values in localStorage, so the next start has them before the network answers. It fetches again when the user signs in or out and every minute (refreshInterval in seconds, 0 to only refresh when you call flags.refresh()). flags.detail(key) gives the value with the variation and the reason (off, rule:0, default, default:split), useful while testing.

Not signed-in visitors get a random id kept in localStorage, so a percentage rollout keeps them on the same side too; after they sign in, their user id is used.

#On your server

With the service key you can evaluate for any user, and you also get flags marked server only (cleared Apps can read it), e.g. a price or a provider switch the browser should not see:

JavaScript
const admin = createClient(URL, SERVICE_KEY)
const { data, error } = await admin.flags.evaluate(
  { id: user.id, email: user.email, app_metadata: user.app_metadata },
  { country: 'DE' },
)
if (data['new-pricing']?.value) { /* … */ }

#Over HTTP

bash
curl -X POST 'https://<ref>.p.stitchdb.com/flags/v1/evaluate' \
  -H "apikey: $ANON_KEY" -H "Authorization: Bearer $USER_JWT" \
  -H 'Content-Type: application/json' \
  -d '{"attributes": {"app_version": "2.4.1"}, "anon_id": "device-123"}'

{"flags": {"new-checkout": {"value": true, "variation": 0, "reason": "rule:0"}}, "version": 12, "user_id": "6c1f…"}

GET /flags/v1/evaluate?attributes=<JSON>&anon_id=…&keys=a,b works too. Send keys to get only some flags. With the service key, add "user": { "id", "email", "app_metadata", "user_metadata" } to evaluate for that user.

#Targeting

When a flag is on, its rules are tried from the top and the first one that matches decides; users no rule matches get the value under Everyone else. A rule matches when the user is in its segment (if it has one) and all its conditions hold. Each rule, and Everyone else, serves one value or a percentage split.

#Attributes

AttributeFrom
user.id, user.email, user.phone, user.role, user.is_anonymousThe session (trusted)
user.app_metadata.<field>The session; only your server can set app metadata, so it is the place for a plan or a team
user.user_metadata.<field>The session; users can change their own user metadata
attr.<name>What the app sends in attributes (not trusted: fine for targeting, not for protecting data)

Operators: is, is not, is one of, is not one of, contains, starts with, ends with, > ≥ < ≤ (numbers), version ≥ and version < (2.4, 2.10.1), before and after (dates), is set, is not set. Separate several values with commas. When the attribute is a list (e.g. user.app_metadata.roles), a condition matches if any item does.

#Segments

A segment is a group several flags can target: beta testers, staff, customers in a country. It has conditions, and lists of user ids to always include or always leave out. A segment cannot be deleted while a rule uses it.

#Percentage rollouts

A split puts each user in a bucket from a hash of the flag and the user id (or the visitor id), so the same user always gets the same value. Raising a rollout from 10% to 30% keeps the first 10% on and adds 20% more. Archiving a flag and restoring it keeps its buckets.

#Test a user

Test a user shows what every flag gives a user of your project (by e-mail or id) or a visitor, with attributes you type, and why. Use it before turning a flag on or changing a rule. Test requests are not counted.

#History and undo

Every change to a flag or a segment is logged with who made it and the flag before and after. What changed shows the difference; Undo puts the flag back to how it was before that change, which is logged as a new change. Changes reach apps on their next refresh, within about a minute.

#Cleaning up

When a feature is out for everyone, remove the flag from your code, then Archive it: apps stop getting it and use the fallback in their code, which also shows you if something still reads it. An archived flag can be restored or deleted. Turning feature flags off for the project stops the evaluate endpoint; apps keep their last values, then the fallbacks. Flags, segments and history are kept.

#Usage and limits

Each call to /flags/v1/evaluate (one load or refresh) is one flag request, however many flags it returns. Once the project has used its plan's requests for the month, the endpoint answers 429 and the SDK keeps serving the last values until the next month. Studio shows how often each value was given out in the last 30 days.

LimitFreeProScaleEnterprise
Feature flags per project101005005,000
Feature flag requests per project per month100,0005,000,00050,000,000100,000,000