# Schema

Your project's schema defines your:

* Flags, i.e. their names and types
* Input types for targeting logic, e.g. `User`, `Organization`, `Environment`, etc.
* [Event types](https://docs.hypertune.com/concepts/event-types), e.g. `SignUpEvent`, `PurchaseEvent`, etc.

You can build your schema visually in the Schema view or write it in [GraphQL](https://graphql.org/learn/schema/). For example, the following schema defines:

* A `Boolean` feature flag called `showNewEditor`
* Input types called `User` and `Organization` to use in flag targeting logic
* An event type called `PurchaseEvent` that captures the top-level `context` and additional `properties` in its payload
* A `Void` event trigger flag called `purchase` which we can use to log the `PurchaseEvent`

```graphql
type Source {
  root(context: Context!): Root!
}

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

enum Environment { development, test, production }

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

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

enum Plan { free, pro, enterprise }

input PurchaseEvent @event  {
  context: Context!
  properties: PurchaseEventProperties!
}

type PurchaseEventProperties {
  revenue: Int!
}

type Root {
  showNewEditor: Boolean!
  purchase(properties: PurchaseEventProperties!): 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:

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

enum Role { viewer, 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:

```graphql
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.

If a targeting attribute is only relevant to a specific flag, this 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 provided `usageAmount`:

```graphql
type Root {
  priceId(usageAmount: Int!): String!
}
```

## Flag lifecycle

You can manage [flag lifecycle](#flag-lifecycle) by marking flags as deprecated in your schema from the UI or in GraphQL with the `@deprecated` directive.

## Schema migrations

To make large changes to your schema, e.g. sweeping refactors, you can use branches and pull requests to easily [migrate your schema](https://docs.hypertune.com/git-based-version-control#schema-migrations).
