-
Notifications
You must be signed in to change notification settings - Fork 2k
docs: add guide on directives #4401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,3 +118,4 @@ words: | |
- XXXF | ||
- bfnrt | ||
- wrds | ||
- debuggable |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
--- | ||
title: Using Directives in GraphQL.js | ||
sidebarTitle: Using Directives | ||
--- | ||
|
||
# Using Directives in GraphQL.js | ||
|
||
Directives let you customize query execution at a fine-grained level. They act like | ||
annotations in a GraphQL document, giving the server instructions about whether to | ||
include a field, how to format a response, or how to apply custom behavior. | ||
|
||
GraphQL.js supports built-in directives like `@include`, `@skip`, and `@deprecated` out | ||
of the box. If you want to create your own directives and apply custom behavior, you'll | ||
need to implement the logic yourself. | ||
|
||
This guide covers how GraphQL.js handles built-in directives, how to define and apply | ||
custom directives, and how to implement directive behavior during execution. | ||
|
||
## How GraphQL.js handles built-in directives | ||
|
||
GraphQL defines built-in directives that control query execution or describe metadata | ||
about the schema. These include: | ||
|
||
- `@include` and `@skip`: Conditionally include or skip fields | ||
- `@deprecated`: Marks fields or enum values as deprecated | ||
|
||
In GraphQL.js, these directives are handled automatically by the executor or introspection system. | ||
You don't need to write custom logic to support them. | ||
|
||
For example, the `@include` directive conditionally includes a field based on a Boolean variable: | ||
|
||
```graphql | ||
query($shouldInclude: Boolean!) { | ||
greeting @include(if: $shouldInclude) | ||
} | ||
``` | ||
|
||
At runtime, GraphQL.js evaluates the `if` argument. If `shouldInclude` is `false`, the | ||
`greeting` field in this example is skipped entirely and your resolver won't run. | ||
|
||
```js | ||
import { graphql, buildSchema } from 'graphql'; | ||
|
||
const schema = buildSchema(` | ||
type Query { | ||
greeting: String | ||
} | ||
`); | ||
|
||
const rootValue = { | ||
greeting: () => 'Hello!', | ||
}; | ||
|
||
const query = ` | ||
query($shouldInclude: Boolean!) { | ||
greeting @include(if: $shouldInclude) | ||
} | ||
`; | ||
|
||
const variables = { shouldInclude: true }; | ||
|
||
const result = await graphql({ | ||
schema, | ||
source: query, | ||
rootValue, | ||
variableValues: variables, | ||
}); | ||
|
||
console.log(result); | ||
// → { data: { greeting: 'Hello!' } } | ||
``` | ||
|
||
If `shouldInclude` is `false`, the result would be `{ data: {} }`. | ||
|
||
The `@deprecated` directive is used in the schema to indicate that a field or enum | ||
value should no longer be used. It doesn't affect execution, but is included | ||
in introspection output: | ||
|
||
```graphql | ||
{ | ||
__type(name: "MyType") { | ||
fields { | ||
name | ||
isDeprecated | ||
deprecationReason | ||
} | ||
} | ||
} | ||
``` | ||
|
||
GraphQL.js automatically includes deprecation metadata in introspection. Tools like | ||
GraphiQL use this to show warnings, but GraphQL.js itself doesn't block or modify behavior. | ||
You can still query deprecated fields unless you add validation rules yourself. | ||
|
||
## Declaring custom directives in GraphQL.js | ||
|
||
To use a custom directive, you first define it in your schema using the | ||
`GraphQLDirective` class. This defines the directive's name, where it can | ||
be applied, and any arguments it accepts. | ||
|
||
A directive in GraphQL.js is just metadata. It doesn't perform any behavior on its own. | ||
|
||
Here's a basic example that declares an `@uppercase` directive that can be applied to fields: | ||
|
||
```js | ||
import { | ||
GraphQLDirective, | ||
DirectiveLocation, | ||
GraphQLNonNull, | ||
GraphQLBoolean, | ||
} from 'graphql'; | ||
|
||
const UppercaseDirective = new GraphQLDirective({ | ||
name: 'uppercase', | ||
description: 'Converts the result of a field to uppercase.', | ||
locations: [DirectiveLocation.FIELD], | ||
args: { | ||
enabled: { | ||
type: GraphQLNonNull(GraphQLBoolean), | ||
defaultValue: true, | ||
description: 'Whether to apply the transformation.', | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
To make the directive available to your schema, you must explicitly include it: | ||
|
||
```js | ||
import { GraphQLSchema } from 'graphql'; | ||
|
||
const schema = new GraphQLSchema({ | ||
query: QueryType, | ||
directives: [UppercaseDirective], | ||
}); | ||
``` | ||
|
||
Once added, tools like validation and introspection will recognize it. | ||
|
||
## Applying directives in queries | ||
|
||
After defining and adding your directive to the schema, clients can apply it in queries using | ||
the `@directiveName` syntax. Arguments are passed in parentheses, similar to field arguments. | ||
|
||
You can apply directives to: | ||
|
||
- Fields | ||
- Fragment spreads | ||
- Inline fragments | ||
|
||
The following examples show how to apply directives: | ||
|
||
```graphql | ||
# Applied to a field | ||
{ | ||
greeting @uppercase | ||
} | ||
``` | ||
|
||
```graphql | ||
# Applied to a fragment spread | ||
{ | ||
...userFields @include(if: true) | ||
} | ||
``` | ||
|
||
```graphql | ||
# Applied to an inline fragment | ||
{ | ||
... on User @skip(if: false) { | ||
} | ||
} | ||
``` | ||
|
||
When a query is parsed, GraphQL.js includes directive nodes in the field's | ||
Abstract Syntax Tree (AST). You can access these via `info.fieldNodes` inside | ||
a resolver. | ||
|
||
## Implementing custom directive behavior | ||
|
||
GraphQL.js doesn't execute custom directive logic for you. You must handle it during | ||
execution. There are two common approaches: | ||
|
||
### 1. Handle directives in resolvers | ||
|
||
Inside a resolver, use the `info` object to access AST nodes and inspect directives. | ||
You can check whether a directive is present and change behavior accordingly. | ||
|
||
```js | ||
import { | ||
graphql, | ||
buildSchema, | ||
getDirectiveValues, | ||
} from 'graphql'; | ||
|
||
const schema = buildSchema(` | ||
directive @uppercase(enabled: Boolean = true) on FIELD | ||
|
||
type Query { | ||
greeting: String | ||
} | ||
`); | ||
|
||
const rootValue = { | ||
greeting: (source, args, context, info) => { | ||
const directive = getDirectiveValues( | ||
schema.getDirective('uppercase'), | ||
info.fieldNodes[0], | ||
info.variableValues | ||
); | ||
|
||
const result = 'Hello, world'; | ||
|
||
if (directive?.enabled) { | ||
return result.toUpperCase(); | ||
} | ||
|
||
return result; | ||
}, | ||
}; | ||
|
||
const query = ` | ||
query { | ||
greeting @uppercase | ||
} | ||
`; | ||
|
||
const result = await graphql({ schema, source: query, rootValue }); | ||
console.log(result); | ||
// → { data: { greeting: 'HELLO, WORLD' } } | ||
``` | ||
|
||
### 2. Use AST visitors or schema wrappers | ||
|
||
For more complex logic, you can preprocess the schema or query using AST visitors or wrap | ||
field resolvers. This lets you inject directive logic globally across | ||
multiple types or fields. | ||
|
||
This approach is useful for: | ||
|
||
- Authorization | ||
- Logging | ||
- Schema transformations | ||
- Feature flags | ||
|
||
## Use cases for custom directives | ||
|
||
Some common use cases for custom directives include: | ||
|
||
- **Formatting**: `@uppercase`, `@date(format: "YYYY-MM-DD")`, `@currency` | ||
- **Authorization**: `@auth(role: "admin")` to protect fields | ||
- **Feature flags**: `@feature(name: "newHeader")` to expose experimental features | ||
- **Observability**: `@log`, `@tag(name: "important")`, or `@metrics(id: "xyz")` | ||
- **Execution control**: Mask or transform fields at runtime with schema visitors | ||
|
||
## Best practices | ||
|
||
When working with custom directives in GraphQL.js, keep the following best practices in mind: | ||
|
||
- GraphQL.js doesn't have a directive middleware system. All custom directive logic must be implemented | ||
manually. | ||
- Weigh schema-driven logic against resolver logic. Directives can make queries more expressive, but they | ||
may also hide behavior from developers reading the schema or resolvers. | ||
- Keep directive behavior transparent and debuggable. Since directives are invisible at runtime unless | ||
logged or documented, try to avoid magic behavior. | ||
- Use directives when they offer real value. Avoid overusing directives to replace things that could be | ||
handled more clearly in schema design or resolvers. | ||
- Validate directive usage explicitly if needed. If your directive has rules or side effects, consider | ||
writing custom validation rules to enforce correct usage. | ||
|
||
## Additional resources | ||
|
||
- [GraphQL Specification: Directives](https://spec.graphql.org/draft/#sec-Language.Directives) | ||
- The Guild's guide on [Schema Directives](https://the-guild.dev/graphql/tools/docs/schema-directives) | ||
- Apollo Server's guide on [Directives](https://www.apollographql.com/docs/apollo-server/schema/directives) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.