Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/flagship/patterns.md
1# Flagship Patterns & Best Practices23## Evaluating Flags in Workers (Binding)45### Simple Boolean Toggle67```typescript8export default {9async fetch(request: Request, env: Env): Promise<Response> {10const showNewUI = await env.FLAGS.getBooleanValue("new-ui", false, {11userId: "user-42",12});1314if (showNewUI) {15return new Response("New UI");16}17return new Response("Classic UI");18},19};20```2122### Multi-Variant String Flag2324```typescript25const checkoutFlow = await env.FLAGS.getStringValue(26"checkout-flow",27"original",28{ userId, country: "US" },29);3031switch (checkoutFlow) {32case "streamlined":33return handleStreamlined(request);34case "one-click":35return handleOneClick(request);36default:37return handleOriginal(request);38}39```4041### JSON Config Flag4243```typescript44interface RateLimitConfig {45rpm: number;46burst: number;47}4849const limits = await env.FLAGS.getObjectValue<RateLimitConfig>(50"rate-limits",51{ rpm: 100, burst: 20 },52{ plan: userPlan },53);54```5556### Using Details for Observability5758```typescript59const details = await env.FLAGS.getBooleanDetails("new-checkout", false, {60userId: "user-42",61});6263console.log(details.value); // true64console.log(details.variant); // "on"65console.log(details.reason); // "TARGETING_MATCH"66console.log(details.errorCode); // undefined (no error)67```6869---7071## Evaluating Flags with OpenFeature (Workers)7273### Binding Passthrough (Recommended)7475```typescript76import { OpenFeature } from "@openfeature/server-sdk";77import { FlagshipServerProvider } from "@cloudflare/flagship";7879export default {80async fetch(request: Request, env: Env): Promise<Response> {81await OpenFeature.setProviderAndWait(82new FlagshipServerProvider({ binding: env.FLAGS }),83);84const client = OpenFeature.getClient();8586const enabled = await client.getBooleanValue("new-checkout", false, {87targetingKey: "user-42",88plan: "enterprise",89country: "US",90});9192return new Response(enabled ? "New checkout" : "Standard checkout");93},94};95```9697### Migration from Another Provider9899Only the provider initialization changes — evaluation call sites stay the same:100101```typescript102// ❌ Before (LaunchDarkly)103await OpenFeature.setProviderAndWait(104new LaunchDarklyProvider({ sdkKey: "..." }),105);106107// ✅ After (Flagship)108await OpenFeature.setProviderAndWait(109new FlagshipServerProvider({ binding: env.FLAGS }),110);111112// Evaluation code is unchanged113const enabled = await client.getBooleanValue("my-flag", false, {114targetingKey: "user-42",115});116```117118---119120## Managing Flags via REST API121122All examples use `api.cloudflare.com`. Set `CLOUDFLARE_ACCOUNT_ID`, `FLAGSHIP_APP_ID`, and `CLOUDFLARE_API_TOKEN` first.123124### Create a Boolean Flag125126```bash127curl -s -X POST \128-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \129-H "Content-Type: application/json" \130-d '{131"key": "new-feature",132"default_variation": "off",133"variations": { "on": true, "off": false },134"rules": [],135"description": "Enable the new feature",136"enabled": false137}' \138"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags" | jq .139```140141### Create a Flag with Internal-Only Targeting142143```bash144curl -s -X POST \145-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \146-H "Content-Type: application/json" \147-d '{148"key": "beta-feature",149"default_variation": "off",150"variations": { "on": true, "off": false },151"rules": [152{153"priority": 1,154"conditions": [155{ "attribute": "email", "operator": "ends_with", "value": "@cloudflare.com" }156],157"serve_variation": "on"158}159],160"description": "Beta feature for internal users",161"enabled": true162}' \163"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags" | jq .164```165166### Create a JSON Config Flag167168```bash169curl -s -X POST \170-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \171-H "Content-Type: application/json" \172-d '{173"key": "rate-limits",174"default_variation": "standard",175"variations": {176"standard": { "rpm": 100, "burst": 20 },177"premium": { "rpm": 1000, "burst": 200 }178},179"rules": [180{181"priority": 1,182"conditions": [183{ "attribute": "plan", "operator": "in", "value": ["enterprise", "business"] }184],185"serve_variation": "premium"186}187],188"description": "Rate limit configuration by plan",189"enabled": true190}' \191"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags" | jq .192```193194### Read a Flag195196```bash197curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \198"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags/new-feature" | jq .199```200201### List All Flags (with pagination)202203```bash204curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \205"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags?limit=50" | jq .206```207208If `nextCursor` is non-null, fetch the next page:209210```bash211curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \212"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags?limit=50&cursor=<nextCursor>" | jq .213```214215### Update a Flag (Full Replace)216217Updates use PUT with the full `FlagDefinition`. Always GET first, modify, then PUT back.218219```bash220# 1. Read current flag221FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \222"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags/new-feature" | jq '.data')223224# 2. Modify (e.g., enable the flag)225UPDATED=$(echo "$FLAG" | jq '.enabled = true')226227# 3. PUT back228echo "$UPDATED" | curl -s -X PUT \229-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \230-H "Content-Type: application/json" \231-d @- \232"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags/new-feature" | jq .233```234235### Toggle a Flag On236237Read-modify-write to set `enabled: true`:238239```bash240BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"241242FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/new-feature" | jq '.data')243UPDATED=$(echo "$FLAG" | jq '.enabled = true')244echo "$UPDATED" | curl -s -X PUT \245-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \246-H "Content-Type: application/json" \247-d @- "$BASE/new-feature" | jq .248```249250### Toggle a Flag Off (Disable)251252Same pattern, set `enabled: false`. The flag immediately returns its default variation for all evaluations.253254```bash255BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"256257FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/new-feature" | jq '.data')258UPDATED=$(echo "$FLAG" | jq '.enabled = false')259echo "$UPDATED" | curl -s -X PUT \260-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \261-H "Content-Type: application/json" \262-d @- "$BASE/new-feature" | jq .263```264265### Add a Targeting Rule to an Existing Flag266267Append a rule to the existing rules array. Pick a priority that doesn't collide with existing rules.268269```bash270BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"271272FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/new-feature" | jq '.data')273UPDATED=$(echo "$FLAG" | jq '.rules += [{274"priority": 2,275"conditions": [{ "attribute": "plan", "operator": "equals", "value": "enterprise" }],276"serve_variation": "on"277}]')278echo "$UPDATED" | curl -s -X PUT \279-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \280-H "Content-Type: application/json" \281-d @- "$BASE/new-feature" | jq .282```283284### Change Rollout Percentage285286Update the rollout percentage on an existing rule (e.g., rule at index 0):287288```bash289BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"290291FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/gradual-rollout" | jq '.data')292UPDATED=$(echo "$FLAG" | jq '.rules[0].rollout.percentage = 50')293echo "$UPDATED" | curl -s -X PUT \294-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \295-H "Content-Type: application/json" \296-d @- "$BASE/gradual-rollout" | jq .297```298299### Change Default Variation300301```bash302BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"303304FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/new-feature" | jq '.data')305UPDATED=$(echo "$FLAG" | jq '.default_variation = "on"')306echo "$UPDATED" | curl -s -X PUT \307-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \308-H "Content-Type: application/json" \309-d @- "$BASE/new-feature" | jq .310```311312### Add a New Variation313314```bash315BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"316317FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/checkout-flow" | jq '.data')318UPDATED=$(echo "$FLAG" | jq '.variations["treatment-c"] = "minimal"')319echo "$UPDATED" | curl -s -X PUT \320-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \321-H "Content-Type: application/json" \322-d @- "$BASE/checkout-flow" | jq .323```324325### Remove a Rule326327Remove a rule by filtering on priority:328329```bash330BASE="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags"331332FLAG=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "$BASE/new-feature" | jq '.data')333UPDATED=$(echo "$FLAG" | jq '.rules = [.rules[] | select(.priority != 2)]')334echo "$UPDATED" | curl -s -X PUT \335-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \336-H "Content-Type: application/json" \337-d @- "$BASE/new-feature" | jq .338```339340### Delete a Flag341342```bash343curl -s -X DELETE \344-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \345"https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/flagship/apps/$FLAGSHIP_APP_ID/flags/old-feature" | jq .346```347348---349350## Targeting Rule Patterns351352### Enterprise-Only Access353354```json355{356"priority": 1,357"conditions": [358{ "attribute": "plan", "operator": "equals", "value": "enterprise" }359],360"serve_variation": "on"361}362```363364### Country-Based Targeting with Logical AND/OR365366Target enterprise users in the US or Canada:367368```json369{370"priority": 1,371"conditions": [372{373"logical_operator": "AND",374"clauses": [375{ "attribute": "plan", "operator": "equals", "value": "enterprise" },376{377"logical_operator": "OR",378"clauses": [379{ "attribute": "country", "operator": "equals", "value": "US" },380{ "attribute": "country", "operator": "equals", "value": "CA" }381]382}383]384}385],386"serve_variation": "on"387}388```389390### Percentage Rollout391392Gradually roll out to 10% of users:393394```json395{396"priority": 1,397"conditions": [398{ "attribute": "targetingKey", "operator": "not_equals", "value": "" }399],400"serve_variation": "on",401"rollout": {402"percentage": 10,403"attribute": "targetingKey"404}405}406```407408### Progressive Rollout Workflow4094101. Create flag with 5% rollout, enable it4112. Monitor metrics4123. Increase to 25% → 50% → 100% by updating the `rollout.percentage`4134. Once at 100%, remove the rule and set `default_variation` to the winning variation4145. Eventually remove the flag and the code branch415416---417418## Safe Deletion Workflow4194201. **Disable** the flag first (`enabled: false`) — confirms nothing depends on it being active4212. **Monitor** for unexpected behavior4223. **Remove** flag evaluation code from your application4234. **Deploy** the code change4245. **Delete** the flag via API425