Skip to content

Edits to short directives article #4979

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 4 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/api/apollo-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ The default value is `true`, **unless** the `NODE_ENV` environment variable is s

<td>

A map of all [custom schema directives](../schema/directives/#using-custom-schema-directives) used in your schema, if any.
A map of all [custom schema directives](../schema/directives/#custom-schema-directives) used in your schema, if any.
</td>
</tr>

Expand Down
93 changes: 67 additions & 26 deletions docs/source/schema/directives.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,88 @@
---
title: Using schema directives
description: Using schema directives to transform schema types, fields, and arguments
title: Directives
sidebar_title: Directives
description: Configure GraphQL types, fields, and arguments
---

A _directive_ is an identifier preceded by a `@` character, optionally followed by a list of named arguments, which can appear after almost any form of syntax in the GraphQL query or schema languages. Here's an example from the [GraphQL draft specification](http://facebook.github.io/graphql/draft/#sec-Type-System.Directives) that illustrates several of these possibilities:
A **directive** decorates part of a GraphQL schema or operation with additional configuration. Tools like Apollo Server (and [Apollo Client](https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#querying)) can read a GraphQL document's directives and perform custom logic as appropriate.

```typescript
Directives are preceded by the `@` character, like so:

```graphql{2}:title=schema.graphql
type ExampleType {
oldField: String @deprecated(reason: "Use `newField`.")
newField: String
}
```

This example shows the `@deprecated` directive, which is a [default directive](#default-directives) (i.e., it's part of the [GraphQL specification](http://spec.graphql.org/June2018/#sec--deprecated)). It demonstrates the following about directives:

* Directives can take arguments of their own (`reason` in this case).
* Directives appear _after_ the declaration of what they decorate (the `oldField` field in this case)

## Valid locations

Each directive can only appear in _certain_ locations within a GraphQL schema or operation. These locations are listed in the directive's definition.

For example, here's the GraphQL spec's definition of the `@deprecated` directive:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may or may not be interesting that this is likely to change to add some more locations (graphql/graphql-spec#805) and that in fact graphql-js and others already support more locations...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, hmm. I'll keep like this for now to match 2018 and might revisit this with additional perspective after cleaning up the big article


```graphql
directive @deprecated(
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
```

type ExampleType {
newField: String
oldField: String @deprecated(reason: "Use `newField`.")
This indicates that `@deprecated` can decorate either a schema `FIELD_DEFINITION` (as shown at the top of the article) or a schema `ENUM_VALUE` definition (as shown here):

```graphql:title=schema.graphql
enum MyEnum {
OLD_VALUE @deprecated(reason: "Use `NEW_VALUE`.")
NEW_VALUE
}
```

As you can see, the usage of `@deprecated(reason: ...)` _follows_ the field that it pertains to (`oldField`), though the syntax might remind you of "decorators" in other languages, which usually appear on the line above. Directives are typically _declared_ once, using the `directive @deprecated ... on ...` syntax, and then _used_ zero or more times throughout the schema document, using the `@deprecated(reason: ...)` syntax.
If `@deprecated` appears elsewhere in a GraphQL document, it produces an error.

> If you [create a custom directive](#custom-schema-directives), you need to define it (and its valid locations) in your schema. You don't need to define [default directives](#default-directives) like `@deprecated`.

### Schema directives vs. operation directives

Usually, a given directive appears _exclusively_ in GraphQL schemas or _exclusively_ in GraphQL operations (rarely both, although the spec allows this).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the implementing directives page calls these query directives. I think moving towards operation rather than query is good, though, and I think you said you were heading to that page next.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I'll replicate this change over there.


For example, among the [default directives](#default-directives), `@deprecated` is a schema-exclusive directive and `@skip` and `@include` are operation-exclusive directives.

The [GraphQL spec](https://spec.graphql.org/June2018/#sec-Type-System.Directives) lists all possible directive locations. Schema locations are listed under `TypeSystemDirectiveLocation`, and operation locations are listed under `ExecutableDirectiveLocation`.

## Default directives

The [GraphQL specification](http://spec.graphql.org/June2018/#sec-Type-System.Directives) defines the following default directives:

## Default Directives
| Directive | Description |
|-----------|-------------|
| `@deprecated(reason: String)` | Marks the schema definition of a field or enum value as deprecated with an optional reason. |
| `@skip(if: Boolean!)` | If `true`, the decorated field or fragment in an operation is _not_ resolved by the GraphQL server. |
| `@include(if: Boolean!)` | If `false`, the decorated field or fragment in an operation is _not_ resolved by the GraphQL server. |

GraphQL provides several default directives: [`@deprecated`](http://facebook.github.io/graphql/draft/#sec--deprecated), [`@skip`](http://facebook.github.io/graphql/draft/#sec--skip), and [`@include`](http://facebook.github.io/graphql/draft/#sec--include).
## Custom schema directives

* [`@deprecated`](http://facebook.github.io/graphql/draft/#sec--deprecated)`(reason: String)` - marks field as deprecated with message
* [`@skip`](http://facebook.github.io/graphql/draft/#sec--skip)`(if: Boolean!)` - GraphQL execution skips the field if true by not calling the resolver
* [`@include`](http://facebook.github.io/graphql/draft/#sec--include)`(if: Boolean!)` - Calls resolver for annotated field if true
You can extend Apollo Server with custom schema directives created by you or a third party.

## Using custom schema directives
> To learn how to create custom directives, see [implementing directives](./creating-directives/).

To use a custom schema directive, pass the implemented class to Apollo Server via the `schemaDirectives` argument, which is an object that maps directive names to directive implementations:
To use a custom directive:

```js
1. Make sure the directive is defined in your schema with all valid locations listed.
2. If the directive uses a `SchemaDirectiveVisitor` subclass to perform custom logic, provide it to the `ApolloServer` constructor via the `schemaDirectives` object.

_The `schemaDirectives` object maps the name of a directive (e.g., `upper`) to the subclass that implements its behavior (e.g., `UpperCaseDirective`)._

The following example defines an `UpperCaseDirective` subclass for use with the `@upper` custom directive. Because it's decorated with `@upper`, the `Query.hello` field returns `HELLO WORLD!` instead of `Hello world!`.

```js{20,40-42}
const { ApolloServer, gql, SchemaDirectiveVisitor } = require('apollo-server');
const { defaultFieldResolver } = require('graphql');

// Create (or import) a custom schema directive
// Subclass definition for @upper directive logic
class UpperCaseDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
Expand All @@ -48,7 +96,7 @@ class UpperCaseDirective extends SchemaDirectiveVisitor {
}
}

// Construct a schema, using GraphQL schema language
// Schema definition (including custom directive)
const typeDefs = gql`
directive @upper on FIELD_DEFINITION

Expand All @@ -57,7 +105,7 @@ const typeDefs = gql`
}
`;

// Provide resolver functions for your schema fields
// Resolvers
const resolvers = {
Query: {
hello: (parent, args, context) => {
Expand All @@ -78,11 +126,4 @@ const server = new ApolloServer({
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`)
});

```

The implementation of `UpperCaseDirective` takes care of changing the resolver and modifying the schema if necessary.

## Building your own

To learn how to implement your own schema directives, read [this guide](/schema/creating-directives/).