GraphQL Directives Deep Dive — @deprecated, @skip, @include, and Schema Metadata
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
- What are the three built-in GraphQL directives?
- How do you deprecate a field?
- What is the difference between @skip and @include?
- How do you define a custom directive?
- What are directive locations?
Answers:
- @deprecated, @skip, @include.
- Add @deprecated(reason: "Use X instead") to the field definition.
- @skip excludes a field when true; @include includes a field when true.
- Using the directive keyword with locations: directive @name on LOCATION.
- 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
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