From 31305e5ccdf807cedaba00c02f38fabab4e34799 Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Tue, 2 Mar 2021 15:14:11 -0800 Subject: [PATCH 1/4] Edits to short directives article --- docs/source/schema/directives.md | 78 +++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/docs/source/schema/directives.md b/docs/source/schema/directives.md index 834a6960dfa..f0183d2c369 100644 --- a/docs/source/schema/directives.md +++ b/docs/source/schema/directives.md @@ -1,40 +1,73 @@ --- -title: Using schema directives -description: Using schema directives to transform schema types, fields, and arguments +title: Directives +sidebar_title: Directives +description: Configure schema 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 document 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} +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's definition specifies _where_ it can appear in a GraphQL document. For example, here's the GraphQL spec's definition of the `@deprecated` directive: + +```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 in the example above) or an enum value definition (as shown here): + +```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 schema, it produces an error. + +## 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 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 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 +### Creating -## Using custom schema directives +See [Implementing directives](/schema/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: +### Using -```js +You can extend Apollo Server with custom schema directives created by you or a third party. + +To use a custom directive, pass its associated `SchemaDirectiveVisitor` subclass to Apollo Server via the `schemaDirectives` argument. This object maps the name of a directive (e.g., `upper`) to the class that implements its behavior (e.g., `UpperCaseDirective`). + +```js{40-42} const { ApolloServer, gql, SchemaDirectiveVisitor } = require('apollo-server'); const { defaultFieldResolver } = require('graphql'); -// Create (or import) a custom schema directive +// Class definition for an @upper directive class UpperCaseDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const { resolve = defaultFieldResolver } = field; @@ -48,7 +81,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 @@ -57,7 +90,7 @@ const typeDefs = gql` } `; -// Provide resolver functions for your schema fields +// Resolvers const resolvers = { Query: { hello: (parent, args, context) => { @@ -78,11 +111,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/). From 28ddf6a3a0add10e82a7466a430a46d877ba9795 Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Tue, 2 Mar 2021 15:20:59 -0800 Subject: [PATCH 2/4] Fix broken link --- docs/source/api/apollo-server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/api/apollo-server.md b/docs/source/api/apollo-server.md index 941e2a0f18d..71e0ac794bf 100644 --- a/docs/source/api/apollo-server.md +++ b/docs/source/api/apollo-server.md @@ -141,7 +141,7 @@ The default value is `true`, **unless** the `NODE_ENV` environment variable is s -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-directives) used in your schema, if any. From 2e1d666fdadb8ffae48342d39692b53e47ec049d Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Mon, 8 Mar 2021 16:24:31 -0800 Subject: [PATCH 3/4] Incorporate feedback from glasser --- docs/source/schema/directives.md | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/source/schema/directives.md b/docs/source/schema/directives.md index f0183d2c369..d5d19c99c51 100644 --- a/docs/source/schema/directives.md +++ b/docs/source/schema/directives.md @@ -1,14 +1,14 @@ --- title: Directives sidebar_title: Directives -description: Configure schema types, fields, and arguments +description: Configure GraphQL types, fields, and arguments --- -A **directive** decorates part of a GraphQL document 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. +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. Directives are preceded by the `@` character, like so: -```graphql{2} +```graphql{2}:title=schema.graphql type ExampleType { oldField: String @deprecated(reason: "Use `newField`.") newField: String @@ -22,7 +22,9 @@ This example shows the `@deprecated` directive, which is a [default directive](# ## Valid locations -Each directive's definition specifies _where_ it can appear in a GraphQL document. For example, here's the GraphQL spec's definition of the `@deprecated` directive: +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: ```graphql directive @deprecated( @@ -30,16 +32,26 @@ directive @deprecated( ) on FIELD_DEFINITION | ENUM_VALUE ``` -This indicates that `@deprecated` can decorate either a schema field definition (as shown in the example above) or an enum value definition (as shown here): +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 +```graphql:title=schema.graphql enum MyEnum { OLD_VALUE @deprecated(reason: "Use `NEW_VALUE`.") NEW_VALUE } ``` -If `@deprecated` appears elsewhere in a GraphQL schema, it produces an error. +If `@deprecated` appears elsewhere in a GraphQL document, it produces an error. + +> If you [create a custom directive](#creating), 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). + +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 @@ -47,7 +59,7 @@ The [GraphQL specification](http://spec.graphql.org/June2018/#sec-Type-System.Di | Directive | Description | |-----------|-------------| -| `@deprecated(reason: String)` | Marks the definition of a field or enum value as deprecated with an optional reason. | +| `@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. | @@ -63,7 +75,9 @@ You can extend Apollo Server with custom schema directives created by you or a t To use a custom directive, pass its associated `SchemaDirectiveVisitor` subclass to Apollo Server via the `schemaDirectives` argument. This object maps the name of a directive (e.g., `upper`) to the class that implements its behavior (e.g., `UpperCaseDirective`). -```js{40-42} +Also make sure the directive is defined in your schema with all valid locations listed. + +```js{20,40-42} const { ApolloServer, gql, SchemaDirectiveVisitor } = require('apollo-server'); const { defaultFieldResolver } = require('graphql'); From 288360ba44b728ba208f5479d07a2ba3b7a5a9ed Mon Sep 17 00:00:00 2001 From: Stephen Barlow Date: Tue, 9 Mar 2021 15:10:51 -0800 Subject: [PATCH 4/4] Incorporate more feedback from glasser --- docs/source/api/apollo-server.md | 2 +- docs/source/schema/directives.md | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/source/api/apollo-server.md b/docs/source/api/apollo-server.md index 71e0ac794bf..5ff97ee9885 100644 --- a/docs/source/api/apollo-server.md +++ b/docs/source/api/apollo-server.md @@ -141,7 +141,7 @@ The default value is `true`, **unless** the `NODE_ENV` environment variable is s -A map of all [custom schema directives](../schema/directives/#custom-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. diff --git a/docs/source/schema/directives.md b/docs/source/schema/directives.md index d5d19c99c51..98f1fa4f00a 100644 --- a/docs/source/schema/directives.md +++ b/docs/source/schema/directives.md @@ -32,7 +32,7 @@ directive @deprecated( ) on FIELD_DEFINITION | ENUM_VALUE ``` -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): +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 { @@ -43,7 +43,7 @@ enum MyEnum { If `@deprecated` appears elsewhere in a GraphQL document, it produces an error. -> If you [create a custom directive](#creating), you need to define it (and its valid locations) in your schema. You don't need to define [default directives](#default-directives) like `@deprecated`. +> 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 @@ -63,25 +63,26 @@ The [GraphQL specification](http://spec.graphql.org/June2018/#sec-Type-System.Di | `@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. | -## Custom directives +## Custom schema directives -### Creating +You can extend Apollo Server with custom schema directives created by you or a third party. -See [Implementing directives](/schema/creating-directives/). +> To learn how to create custom directives, see [implementing directives](./creating-directives/). -### Using +To use a custom directive: -You can extend Apollo Server with custom schema directives created by you or a third party. +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. -To use a custom directive, pass its associated `SchemaDirectiveVisitor` subclass to Apollo Server via the `schemaDirectives` argument. This object maps the name of a directive (e.g., `upper`) to the class that implements its behavior (e.g., `UpperCaseDirective`). + _The `schemaDirectives` object maps the name of a directive (e.g., `upper`) to the subclass that implements its behavior (e.g., `UpperCaseDirective`)._ -Also make sure the directive is defined in your schema with all valid locations listed. +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'); -// Class definition for an @upper directive +// Subclass definition for @upper directive logic class UpperCaseDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const { resolve = defaultFieldResolver } = field;