Skip to content

Directives in GraphQL — Complete Guide

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Directives in Graphql. We cover key concepts, practical examples, and best practices to help you master this topic.

Directives are annotations in GraphQL that modify the behavior of schema elements or query execution. Built-in directives like @deprecated, @skip, and @include handle common cases. Custom schema directives extend GraphQL for authentication, formatting, and validation.

What You'll Learn

  • Built-in directives: @deprecated, @skip, @include, @specifiedBy
  • Custom directive definition with directive declarations
  • Directive locations: FIELD, FIELD_DEFINITION, OBJECT, etc.
  • Implementing custom directives in resolvers

Why It Matters

Directives keep schemas clean by separating cross-cutting concerns from type definitions. @deprecated enables graceful API evolution. Custom directives reduce boilerplate in resolvers.

Real-World Use

Apollo Server uses directives for authentication (@auth), Rate Limiting (@rateLimit), and access control. The GraphQL spec includes built-in directives for conditional execution and deprecation.

flowchart LR
    Directive[GraphQL Directive] --> BuiltIn[Built-in]
    Directive --> Custom[Custom]
    BuiltIn --> deprecated[@deprecated]
    BuiltIn --> skip[@skip]
    BuiltIn --> include[@include]
    Custom --> auth[@auth]
    Custom --> format[@format]
    Custom --> validate[@validate]

Teacher Mindset

Think of directives as decorators that add metadata or behavior to schema elements without changing their core definition. Schema directives run at query time. Executable directives run on the client side.

Code Examples

# Example 1: Built-in directives
type User {
  id: ID!
  name: String!
  email: String @deprecated(reason: "Use emailAddress instead")
  emailAddress: String
}

query GetUsers($showEmail: Boolean!) {
  users {
    name
    email @include(if: $showEmail)
    oldField @skip(if: true)
  }
}
# Example 2: Custom schema directive declaration
directive @auth(
  requires: Role = ADMIN
) on OBJECT | FIELD_DEFINITION

directive @format(
  dateFormat: String = "YYYY-MM-DD"
) on FIELD_DEFINITION

directive @lengthRange(
  min: Int!
  max: Int!
) on INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION

type Query {
  users: [User!]! @auth(requires: USER)
  dashboard: Dashboard @auth(requires: ADMIN)
}
// Example 3: Custom directive implementation
class AuthDirective extends SchemaDirectiveVisitor {
  visitObject(type) {
    this.ensureFieldsWrapped(type);
  }

  visitFieldDefinition(field) {
    const originalResolve = field.resolve || defaultFieldResolver;
    const { requires } = this.args;
    field.resolve = async function (parent, args, context, info) {
      if (!context.user || context.user.role !== requires) {
        throw new Error('Not authorized');
      }
      return originalResolve.call(this, parent, args, context, info);
    };
  }
}

Common Mistakes

  • Overusing @skip and @include when fragments with spread conditions are more readable
  • Creating custom directives that could be handled by resolver logic
  • Not specifying directive locations correctly in declaration
  • Forgetting to implement the Visitor pattern for schema directives
  • Making directives too complex with multiple responsibilities

Practice

  1. Add @deprecated to three fields in your schema with reasons.
  2. Use @skip and @include in a query with boolean variables.
  3. Create a custom @uppercase directive that transforms string fields.
  4. Implement an @auth directive that checks user roles.
  5. Challenge: Build a @cache directive that sets cache TTL on query responses.

FAQ

What is the difference between executable and schema directives?

Executable directives (@skip, @include) are used in client queries. Schema directives (@deprecated, custom) are used in schema definitions.

Can I define custom directives in any GraphQL server?

Yes. Most implementations support SchemaDirectiveVisitor or middleware patterns for custom directives.

What happens when a field with @deprecated is queried?

The server returns the field normally. GraphQL tools and IDEs may show deprecation warnings to developers.

Can arguments be passed to directives?

Yes. Directives accept arguments: @deprecated(reason: 'Use newField'), @auth(requires: ADMIN).

How do I test custom directives?

Write unit tests for the directive visitor logic and integration tests that verify the directive behavior on fields.

Mini Project

Create three custom directives for your schema: @auth for role-based access on sensitive fields, @lowercase for normalizing string inputs, and @log for tracking field access in development.

What's Next

Next, you will learn about interfaces and unions for polymorphic types in GraphQL schemas.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro