Skip to content

GraphQL Directives Deep Dive — @deprecated, @skip, @include, and Schema Metadata

DodaTech Updated 2026-06-28 3 min read

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

GraphQL directives provide a way to attach metadata to schema elements and control query execution behavior, including built-in directives for deprecation and conditional inclusion.

What You'll Learn

  • Using @deprecated, @skip, @include directives
  • Defining custom directives
  • Directive locations and execution

Why It Matters

Directives enable schema evolution, conditional queries, and reusable behavior without changing schema structure. They are essential for maintaining backward compatibility.

Code Examples

# Built-in directives
type User {
  id: ID!
  name: String!
  oldField: String @deprecated(reason: "Use newField instead")
  newField: String
}

type Query {
  users(includeInactive: Boolean = false): [User!]!
    @deprecated(reason: "Use paginated users query instead")
}

# Client-side usage
query GetUsers($showEmail: Boolean!, $excludeInactive: Boolean!) {
  users {
    id
    name
    email @include(if: $showEmail)
    inactiveReason @skip(if: $excludeInactive)
  }
}
# Custom directive definition
directive @auth(required: Role = ADMIN) on FIELD_DEFINITION | OBJECT
directive @rateLimit(max: Int!, window: Int!) on FIELD_DEFINITION
directive @formatDate(format: String = "YYYY-MM-DD") on FIELD_DEFINITION
directive @cacheControl(maxAge: Int!) on FIELD_DEFINITION | OBJECT

type Query {
  adminData: [String!]! @auth(required: ADMIN) @rateLimit(max: 10, window: 60)
  publicData: [String!]! @cacheControl(maxAge: 3600)
}
// Custom directive implementation (Apollo Server)
class AuthDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field) {
    const { required } = this.args;
    const originalResolve = field.resolve || (() => {});

    field.resolve = async function (parent, args, context, info) {
      if (!context.user) {
        throw new AuthenticationError('Not authenticated');
      }
      if (required && context.user.role !== required) {
        throw new ForbiddenError('Insufficient permissions');
      }
      return originalResolve.call(this, parent, args, context, info);
    };
  }
}

Common Mistakes

1. Overusing @deprecated

Only deprecate fields when there is a clear replacement. Avoid cluttering the schema.

2. Not Documenting Deprecation Reasons

Always provide a reason in @deprecated so clients know what to use instead.

3. Creating Too Many Custom Directives

Each directive adds complexity. Only create directives for reusable cross-cutting concerns.

4. Ignoring Directive Locations

Define directives with the correct location restrictions to prevent misuse.

5. Making Directives Too Complex

Directives should do one thing well. Combine multiple directives for complex behavior.

Practice Questions

  1. What are the three built-in GraphQL directives?
  2. How do you deprecate a field?
  3. What is the difference between @skip and @include?
  4. How do you define a custom directive?
  5. What are directive locations?

Answers:

  1. @deprecated, @skip, @include.
  2. Add @deprecated(reason: "Use X instead") to the field definition.
  3. @skip excludes a field when true; @include includes a field when true.
  4. Using the directive keyword with locations: directive @name on LOCATION.
  5. The schema elements where a directive can be applied (FIELD, OBJECT, QUERY, etc.).

Challenge: Create a set of custom directives for a production API: @auth for authorization, @rateLimit for Rate Limiting, @cacheControl for Caching, @log for request logging, and @validate for input validation.

FAQ

Can directives modify query results?

Execution directives like @skip and @include modify the query structure. Schema directives like @deprecated add metadata.

How do directives affect performance?

Custom directives with resolvers add minimal overhead. Complex directive implementations may impact performance.

Can I use directives on input types?

Yes, if the directive location includes INPUT_OBJECT or INPUT_FIELD_DEFINITION.

Are directives part of the GraphQL specification?

Yes, built-in directives are defined in the spec. Custom directives are supported by most GraphQL implementations.

How do I test directives?

Test directive implementations unit tests. For execution directives, test query results with and without the directive.

Mini Project

Build a directives library for an enterprise GraphQL API: @auth (role-based), @rateLimit (configurable window), @cacheControl (max age), @log (audit logging), and @validate (field validation). Implement each directive and write tests.

What's Next

Learn about custom directives for advanced use cases, then explore nested queries and resolver chains.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro