Schema

Your project's schema defines your:

  • Feature flags, i.e. their names and types

  • Input types for targeting logic, e.g. User, Organization, Environment

You can write your schema in GraphQL. For example, the following schema defines:

  • A Boolean feature flag called showNewEditor

  • Input types called User, Organization and Environment to use in flag targeting logic

  • A Void flag to log the Purchase event

type Query {
  root(context: Context!): Root!
}

input Context {
  user: User!
  organization: Organization!
  environment: Environment!
}

input User {
  id: String!
  name: String!
  email: String!
}

input Organization {
  id: String!
  name: String!
  plan: Plan!
}

enum Plan { FREE, PRO, BUSINESS }

enum Environment { DEV, STAGING, PROD }

type Root {
  showNewEditor: Boolean!
  purchase: Void!
}

Complex input types

You can define arbitrarily complex types for your inputs. For example, the User input type can have a field with a list of roles:

input User {
  id: String!
  name: String!
  email: String!
  roles: [Role!]!
}

enum Role { DEVELOPER, EDITOR, ADMIN }

Complex flag types

In addition to simple Boolean feature flags, you can have flags with String, Int and Float types, and custom enum, object and list types. For example, you can define the following flags on the Root type:

type Root {
  showNewEditor: Boolean!
  purchase: Void!
  navBarPosition: Position!
  maxTeamMembers: Int!
  callToActionText: String!
  onboardingGuides: [OnboardingGuide!]!
}

enum Position { LEFT, RIGHT, TOP, BOTTOM }

type OnboardingGuide {
  id: String!
  title: String!
  bodyMarkdown: String!
  checklist: [String!]!
  difficultyLevel: DifficultyLevel!
  expectedDurationSeconds: Int!
}

enum DifficultyLevel { BEGINNER, INTERMEDIATE, ADVANCED }

Flag-specific targeting attributes

Instead of adding new input types and targeting attributes to the top-level Context, you can add them directly to specific flags.

This is useful if a targeting attribute is only relevant to a specific flag, as it avoids polluting the top-level scope with it, and ensures it can't be accidentally used in the targeting logic of other flags.

It also lets you pass different attribute values to a flag in the same browser session. So you can have flags that depend on the session state like user input, selected options, the current view, etc.

For example, you can define a priceId flag that depends on the entered promoCode:

type Root {
  priceId(promoCode: String!): String!
}

Last updated