Put a feature behind a flag
Ship a feature dark and turn it on per user, role, organization or everywhere, without a deploy.
- Register the flag in
src/server/flags.ts, and call the helper first inservices/api/src/server.ts, in the feature-flags block:
New behaviour startsimport { registerFlags } from "@monark/feature-flags/server"; export function registerPostsFeatureFlags(): void { registerFlags("posts", { scheduling: { description: "Schedule posts to publish later.", defaultOn: false }, }); }defaultOn: false. Usetrueonly for a kill switch over something already shipped. - Check it on the server where the feature starts, in the caller's scope so overrides apply:
const on = await isEnabled("posts.scheduling", { userId: ctx.userId, organizationId: org.id }); if (!on) throw new ForbiddenError("Scheduling isn't enabled for this organization.");isEnabledcomes from@monark/feature-flags/server,ForbiddenErrorfrom@monark/common. - In the web app, hide the entry point when the flag is off. A server component can read
featureFlags.getAllForSessionand callnotFound(), asservices/web/src/app/(authed)/wiki/layout.tsxdoes. A navigation entry takes aflagproperty inservices/web/src/config/primary-nav.ts. - Turn it on locally: in
tools/enable-dev-flags.ts, call your register helper next to the others and add the key toDEV_FLAGS, then runpnpm enable:dev-flags.
Good to know:
- The most specific override wins: user, then role, then organization, then global, then
defaultOn. - Hiding a button isn't protection. The server check in step 2 is the boundary.
- Never rename or reuse a shipped flag key.