100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
GraphQL

Directives in GraphQL

Understand built-in and custom GraphQL directives, how they alter query execution and schema behavior, and where to apply them safely.

Advanced GraphQLIntermediate8 min readJul 10, 2026
Analogies

What Directives Do

A directive is a @name(args) annotation attached to a field, fragment, or schema definition that changes how the GraphQL execution engine or schema-building tooling treats that location, without requiring a new keyword in the query language itself. Directives come in two flavors: executable directives, which appear in a query/mutation/subscription document and affect execution — like the built-in @include and @skip — and type-system directives, which appear in the schema definition itself and affect validation, code generation, or resolver behavior, such as @deprecated or custom directives like @auth.

🏏

Cricket analogy: A directive is like a fielding instruction shouted mid-over — 'skip the review' or 'include the replay' — that changes how a specific ball is handled without rewriting the entire match rulebook.

Built-in Executable Directives

The GraphQL specification defines three built-in directives that every spec-compliant server supports out of the box: @include(if: Boolean) includes a field only when the condition is true, @skip(if: Boolean) omits a field when the condition is true, and @deprecated(reason: String) marks a schema field or enum value as deprecated with an explanation surfaced in introspection and tooling like GraphiQL. @include and @skip are evaluated purely on the client's declared variables at query time — they let one query document conditionally shape its own response instead of requiring the client to maintain multiple near-duplicate query strings.

🏏

Cricket analogy: @include(if: $showReplay) is like a broadcast director's toggle to cut to the DRS review footage only when the umpire actually calls for it, avoiding a separate broadcast feed for every possible scenario.

graphql
query PlayerProfile($id: ID!, $showStats: Boolean!, $hideBio: Boolean!) {
  player(id: $id) {
    name
    bio @skip(if: $hideBio)
    careerStats @include(if: $showStats) {
      matches
      average
    }
    legacyRanking @deprecated(reason: "Use eloRating instead")
  }
}

@include and @skip can both be applied to the same field simultaneously; if @skip(if: true) is present, it takes precedence and the field is omitted regardless of the @include condition, per the GraphQL specification's execution rules.

Custom Directives for Cross-Cutting Concerns

Beyond the built-ins, GraphQL lets a schema author define custom directives — commonly type-system directives applied in the SDL, such as @auth(requires: Role), @rateLimit(max: Int, window: String), or @cost(complexity: Int) — that a server implementation reads and enforces through directive resolvers, schema transformers, or middleware wired up in tools like graphql-tools's mapSchema or Apollo Server's schema directives API. This turns cross-cutting concerns like authorization, rate limiting, and field-level cost estimation into declarative annotations sitting right next to the field they govern, instead of scattering imperative checks throughout resolver code.

🏏

Cricket analogy: A custom @auth(requires: SELECTOR) directive is like stamping 'selectors only' on a confidential team-selection document, so the access rule lives right on the document itself instead of a separate rulebook someone has to remember to check.

graphql
directive @auth(requires: Role = USER) on FIELD_DEFINITION

enum Role {
  USER
  ADMIN
}

type Mutation {
  deleteUser(id: ID!): Boolean! @auth(requires: ADMIN)
  updateProfile(input: ProfileInput!): User! @auth(requires: USER)
}

Custom directives declared in SDL do nothing on their own — the directive definition only documents where it's legal to use; the actual enforcement logic must be wired up separately via a schema transformer or directive resolver. Forgetting to implement the enforcement side is a common source of security bugs where a directive like @auth looks like it's protecting a field but silently does nothing at runtime.

Directive Locations and Validation

Every directive definition declares the exact locations it's legal to appear in — FIELD, FIELD_DEFINITION, FRAGMENT_SPREAD, OBJECT, ARGUMENT_DEFINITION, and many more — using the on keyword in its SDL declaration, and the GraphQL validator rejects a query or schema at parse/build time if a directive is used somewhere it isn't declared for. This location system is what keeps directives predictable: @include/@skip are only valid on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT (executable locations), while something like @deprecated is valid on FIELD_DEFINITION | ENUM_VALUE (type-system locations), so a schema author can't accidentally misuse one in the wrong context.

🏏

Cricket analogy: Directive locations are like ICC playing-condition clauses that only apply to specific match formats — a Powerplay rule valid only in T20 and ODI locations is rejected outright if someone tries to invoke it during a Test match.

  • Directives are @name(args) annotations that modify execution or schema behavior without new query-language syntax.
  • Executable directives (@include, @skip) appear in operations and conditionally shape the response at query time.
  • Type-system directives (@deprecated, custom ones) appear in the schema and affect validation, tooling, or resolver behavior.
  • Custom directives like @auth or @rateLimit declaratively express cross-cutting concerns next to the fields they govern.
  • Declaring a custom directive in SDL only documents its legal locations — enforcement logic must be implemented separately.
  • Every directive declares valid locations via on, and the validator rejects misuse outside those locations.
  • @skip(if: true) takes precedence over @include when both are applied to the same field.

Practice what you learned

Was this page helpful?

Topics covered

#GraphQL#GraphQLStudyNotes#WebDevelopment#DirectivesInGraphQL#Directives#Built#Executable#Custom#APIs#StudyNotes#SkillVeris