GraphQL Directives – @include and @skip

Directives modify how the GraphQL server processes fields. The two built-in directives — @include and @skip — let you conditionally include or exclude fields in a query based on a variable. They eliminate the need to build different queries for different UI states.

What a Directive Looks Like

  field @directiveName(argument: value)

  Examples:
  ──────────
  name @include(if: $showName)
  price @skip(if: $hidePrice)
  reviews @include(if: $showReviews) {
    rating
    comment
  }

@include — Show a Field When Condition Is True

@include(if: Boolean) includes the field in the response when the condition is true. When the condition is false, the server skips the field entirely — it does not appear in the response at all.

  Query:
  ───────
  query GetProduct($id: ID!, $showReviews: Boolean!) {
    product(id: $id) {
      name
      price
      reviews @include(if: $showReviews) {
        rating
        comment
      }
    }
  }

  Variables: { "id": "p1", "showReviews": true }
  Response includes reviews:
  ───────────────────────────
  {
    "product": {
      "name": "Noise-Cancelling Headphones",
      "price": 149.99,
      "reviews": [
        { "rating": 5, "comment": "Excellent sound!" }
      ]
    }
  }

  Variables: { "id": "p1", "showReviews": false }
  Response has no reviews key at all:
  ─────────────────────────────────────
  {
    "product": {
      "name": "Noise-Cancelling Headphones",
      "price": 149.99
    }
  }

@skip — Hide a Field When Condition Is True

@skip(if: Boolean) is the reverse of @include. The field is skipped when the condition is true and included when it is false.

  query GetUser($id: ID!, $isGuest: Boolean!) {
    user(id: $id) {
      name
      email @skip(if: $isGuest)     ← hide for guests
      phone @skip(if: $isGuest)     ← hide for guests
    }
  }

  $isGuest: true   → only name is returned
  $isGuest: false  → name, email, and phone are returned

@include vs @skip — Choosing One

  Use @include when your variable name is positive:
    showDetails, withReviews, includeAddress → @include(if: $var)

  Use @skip when your variable name is negative:
    isGuest, hidePrice, skipExtras → @skip(if: $var)

  They are logical inverses:
  @include(if: true)  = @skip(if: false) = field appears
  @include(if: false) = @skip(if: true)  = field disappears

Using Directives on Fragments

Directives also work on fragment spreads. This lets you conditionally include an entire group of fields at once.

  fragment AdminFields on User {
    role
    permissions
    lastLoginAt
  }

  query GetUser($id: ID!, $isAdmin: Boolean!) {
    user(id: $id) {
      name
      email
      ...AdminFields @include(if: $isAdmin)
    }
  }

Real-World Pattern — Feature Flags

Directives work well with feature flags. Your app checks a feature flag and passes the result as a variable. The query fetches extra data only when the feature is enabled — no extra code needed on the server.

  const showBetaWidget = featureFlags.get('beta_widget');

  const query = gql`
    query Dashboard($showBeta: Boolean!) {
      stats { visitors revenue }
      betaMetrics @include(if: $showBeta) {
        experimentGroup
        conversionRate
      }
    }
  `;

  useQuery(query, { variables: { showBeta: showBetaWidget } });

Key Points

  • Directives modify field behavior at query time without changing the schema.
  • @include(if: true) includes the field; @include(if: false) omits it entirely.
  • @skip(if: true) omits the field; @skip(if: false) includes it.
  • Directives accept variables, making conditional field selection fully dynamic.
  • Directives work on individual fields and on fragment spreads.

Leave a Comment