You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/graphql/resolvers-map.md
+48-19Lines changed: 48 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,8 @@ Resolvers provide the instructions for turning a [GraphQL](https://graphql.org/)
6
6
7
7
In the code first approach, we don't follow the typical process of creating our GraphQL schema by writing GraphQL SDL by hand. Instead, we use TypeScript decorators to generate the SDL from TypeScript class definitions. The `@nestjs/graphql` package reads the metadata defined through the decorators and automatically generates the schema for you.
8
8
9
+
#### Object types
10
+
9
11
Most of the definitions in a GraphQL schema are **object types**. Each object type you define should represent a domain object that an application client might need to interact with. For example, our sample API needs to be able to fetch a list of authors and their posts, so we should define the `Author` type and `Post` type to support this functionality.
10
12
11
13
If we were using the schema first approach, we'd define such a schema with SDL like this:
@@ -42,9 +44,9 @@ export class Author {
42
44
}
43
45
```
44
46
45
-
> info **Hint** TypeScript's metadata reflection system has several limitations which make it impossible to, for instance, determine what properties a class consists of or recognize whether a given property is optional or required. Because of these limitations, we must either explicitly use the `@Field` decorator in our schema definition classes, or use a [CLI plugin](/graphql/resolvers#cli-plugin) to generate these for us.
47
+
> info **Hint** TypeScript's metadata reflection system has several limitations which make it impossible, for instance, to determine what properties a class consists of or recognize whether a given property is optional or required. Because of these limitations, we must either explicitly use the `@Field()` decorator in our schema definition classes to provide metadata about each field's GraphQL type and optionality, or use a [CLI plugin](/graphql/resolvers#cli-plugin) to generate these for us.
46
48
47
-
The `Author` object type, like any class, is made of a collection of fields, with each field declaring a type. The types correspond to [GraphQL types](https://graphql.org/learn/schema/). A field's GraphQL type can be either another object type or a scalar type. A GraphQL scalar type is a primitive (like `ID`, `String`, `Boolean`, or `Int`) that resolves to a single value.
49
+
The `Author` object type, like any class, is made of a collection of fields, with each field declaring a type. A field's type corresponds to a [GraphQL type](https://graphql.org/learn/schema/). A field's GraphQL type can be either another object type or a scalar type. A GraphQL scalar type is a primitive (like `ID`, `String`, `Boolean`, or `Int`) that resolves to a single value.
48
50
49
51
> info **Hint** In addition to GraphQL's built-in scalar types, you can define custom scalar types (read [more](/graphql/scalars)).
50
52
@@ -61,7 +63,7 @@ type Author {
61
63
62
64
The `@Field()` decoratoracceptsanoptionaltypefunction (e.g., `type => Int`), andoptionallyanoptionsobject.
63
65
64
-
Thetypefunctionisrequiredwhenthere'sthepotentialforambiguitybetweentheTypeScripttypesystemandtheGraphQLtypesystem. Specifically: itis **not** requiredfor `string` and `boolean` types; it **is** requiredforarrays, numbers (which must be mapped to either an `Int` or a `Float`) andobjecttypes. ThetypefunctionshouldsimplyreturnthedesiredGraphQLtype (as shown in various examples in these chapters).
66
+
Thetypefunctionisrequiredwhenthere'sthepotentialforambiguitybetweentheTypeScripttypesystemandtheGraphQLtypesystem. Specifically: itis **not** requiredfor `string` and `boolean` types; it **is** requiredforarrays, numbers (which must be mapped to either a GraphQL `Int` or `Float`) andobjecttypes. ThetypefunctionshouldsimplyreturnthedesiredGraphQLtype (as shown in various examples in these chapters).
> info **Hint** You can also add a description to, or deprecate, the whole object type: `@ObjectType({{ '{' }} description: 'Author model' {{ '}' }})`.
80
82
81
-
When the field is an array, we must manually indicate the array type in the decorator type function, as shown below:
83
+
When the field is an array, we must manually indicate the array type in the `Field()`decorator's type function, as shown below:
82
84
83
85
```typescript
84
86
@Field(type=> [Post])
85
87
posts: Post[];
86
88
```
87
89
88
-
> info **Hint**With `[ ]`, we can indicate the depth of the array. For example, using `[[Int]]` would represent an integer matrix.
90
+
> info **Hint**Using array bracket notation (`[ ]`), we can indicate the depth of the array. For example, using `[[Int]]` would represent an integer matrix.
89
91
90
92
To declare that an array's items (not the array itself) are nullable, set the `nullable` property to `'items'` as shown below:
asyncauthor(@Args('id', {type: () => Int }) id: number) {
144
146
returnthis.authorsService.findOneById(id);
145
147
}
146
148
@@ -154,21 +156,27 @@ export class AuthorsResolver {
154
156
155
157
> info **Hint** All decorators (e.g., `@Resolver`, `@ResolveField`, `@Args`, etc.) are exported from the `@nestjs/graphql` package.
156
158
157
-
> warning **Warning** The logic inside the `AuthorsService` and `PostsService`classes can be as simple or sophisticated as needed. The main point of this example is to show how to construct resolvers and how they can interact with other providers.
159
+
You can define multiple resolver classes. Nest will combine these at run time. See the [module](/graphql/resolvers#module) section below for more on code organization.
158
160
159
-
In the example above, we created the `AuthorsResolver` which defines one query resolver function and one field resolver function. To create a resolver, we create a class with resolver functions as methods, and we annotate the class with the `@Resolver()` decorator.
161
+
> warning **Note** The logic inside the `AuthorsService` and `PostsService` classes can be as simple or sophisticated as needed. The main point of this example is to show how to construct resolvers and how they can interact with other providers.
160
162
161
-
The argument passed to the `@Resolver()` decorator is optional. However, in our case, since the class includes a **field resolver** function (for the `posts` property of the `Author` object type), it becomes required; in that case, the value is used to indicate which class is the parent type (i.e., the corresponding `ObjectType` class name) for any field resolver defined within this class.
163
+
In the example above, we created the `AuthorsResolver` which defines one query resolver function and one field resolver function. To create a resolver, we create a class with resolver functions as methods, and annotate the class with the `@Resolver()` decorator.
162
164
163
165
In this example, we defined a query handler to get the author object based on the `id` sent in the request. To specify that the method is a query handler, use the `@Query()` decorator.
164
166
167
+
The argument passed to the `@Resolver()` decorator is optional, but comes into play when our graph becomes non-trivial. It's used to supply a parent object used by field resolver functions as they traverse down through an object graph.
168
+
169
+
In our example, since the class includes a **field resolver** function (for the `posts` property of the `Author` object type), we **must** supply the `@Resolver()` decorator with a value to indicate which class is the parent type (i.e., the corresponding `ObjectType` class name) for all field resolvers defined within this class. As should be clear from the example, when writing a field resolver function, it's necessary to access the parent object (the object the field being resolved is a member of). In this example, we populate an author's posts array with a field resolver that calls a service which takes the author's `id` as an argument. Hence the need to identify the parent object in the `@Resolver()` decorator. Note the corresponding use of the `@Parent()` method parameter decorator to then extract a reference to that parent object in the field resolver.
170
+
165
171
We can define multiple `@Query()` resolver functions (both within this class, and in any other resolver class), and they will be aggregated into a single **Query type** definition in the generated SDL along with the appropriate entries in the resolver map. This allows you to define queries close to the models and services that they use, and to keep them well organized in modules.
166
172
173
+
#### Query type names
174
+
167
175
In the above examples, the `@Query()` decorator generates a GraphQL schema query type name based on the method name. For example, consider the following construction from the example above:
asyncgetAuthor(@Args('id', {type: () => Int }) id: number) {
199
207
returnthis.authorsService.findOneById(id);
200
208
}
201
209
@@ -215,31 +223,52 @@ type Query {
215
223
}
216
224
```
217
225
226
+
#### Query decorator options
227
+
218
228
The `@Query()` decorator'soptionsobject (where we pass `{{ '{' }}name: 'author'{{ '}' }}` above) acceptsanumberofkey/valuepairs:
219
229
220
230
- `name`: nameofthequery; a `string`
221
231
- `description`: adescriptionthatwillbeusedtogenerateGraphQLschema documentation (e.g., in GraphQL playground); a `string`
222
232
- `deprecationReason`: sets query metadata to show the query as deprecated (e.g., in GraphQL playground); a `string`
223
233
- `nullable`: whether the query can return a null data response; `boolean` or `'items'` or `'itemsAndList'` (see above for details of `'items'` and `'itemsAndList'`)
224
234
225
-
Usually your `@Args()` decorator will be simple, and not require an object argument as seen with the `getAuthor()` method above. For example, if an identifier's type is string, the following construction is sufficient:
235
+
### Args decorator options
236
+
237
+
Use the `@Args()` decorator to extract arguments from a request for use in the method handler. This works in a very similar fashion to [REST route parameter argument extraction](/controllers#route-parameters).
238
+
239
+
Usually your `@Args()` decorator will be simple, and not require an object argument as seen with the `getAuthor()` method above. For example, if an identifier's type is string, the following construction is sufficient, and simply plucks the named field from the inbound GraphQL request for use as a method argument.
226
240
227
241
```typescript
228
242
@Args('id') id: string
229
243
```
230
244
231
-
However, in this case, the `number` type is used, and it presents a challenge. The `number` TypeScript type doesn't give us enough information about the expected GraphQL representation (e.g., `Int` vs. `Float`). Thus we have to **explicitly** pass the type reference.
245
+
In the `getAuthor()` case, the `number` type is used, which presents a challenge. The `number` TypeScript type doesn't give us enough information about the expected GraphQL representation (e.g., `Int` vs. `Float`). Thus we have to **explicitly** pass the type reference. We do that by passing a second argument to the `Args()` decorator, containing argument options, as shown below:
The options object allows us to specify the following optional key value pairs:
255
+
256
+
-`type`: a function returning the GraphQL type
257
+
-`defaultValue`: a default value; `any`
258
+
-`description`: description metadata; `string`
259
+
-`deprecationReason`: to deprecate a field and provide meta data describing why; `string`
260
+
-`nullable`: whether the field is nullable
232
261
233
262
Query handler methods can take multiple arguments. Let's imagine that we want to fetch an author based on its `firstName` and `lastName`. In this case, we can call `@Args` twice:
> info **Hint** Note that we can pass a second argument to the `@Args()` decorator, which is an options object. The options object allows us to specify a default value (`'defaultValue'` key), description (`'description'` key, ), deprecation reason (`'deprecationReason'` key), or if a field is nullable (`'nullable'` key).
271
+
#### Dedicated arguments class
243
272
244
273
With inline `@Args()` calls, code like the example above becomes bloated. Instead, you can create a dedicated `GetAuthorArgs` arguments class and access it in the handler method as follows:
245
274
@@ -265,7 +294,7 @@ class GetAuthorArgs {
265
294
}
266
295
```
267
296
268
-
> info **Hint** Again, due to TypeScript's metadata reflection system limitations, it's required to either use the `@Field` decorator to manually indicate a type, or use a [CLI plugin](/graphql/resolvers#cli-plugin).
297
+
> info **Hint** Again, due to TypeScript's metadata reflection system limitations, it's required to either use the `@Field` decorator to manually indicate type and optionality, or use a [CLI plugin](/graphql/resolvers#cli-plugin).
269
298
270
299
This will result in generating the following part of the GraphQL schema in SDL:
271
300
@@ -334,7 +363,7 @@ export class AuthorsResolver {
334
363
335
364
> info **Hint** All decorators (e.g., `@Resolver`, `@ResolveField`, `@Args`, etc.) are exported from the `@nestjs/graphql` package.
336
365
337
-
> warning **Warning** The logic inside the `AuthorsService` and `PostsService` classes can be as simple or sophisticated as needed. The main point of this example is to show how to construct resolvers and how they can interact with other providers.
366
+
> warning **Note** The logic inside the `AuthorsService` and `PostsService` classes can be as simple or sophisticated as needed. The main point of this example is to show how to construct resolvers and how they can interact with other providers.
338
367
339
368
The `@Resolver()` decorator is required. It takes an optional string argument with the name of a class. This class name is required whenever the class includes `@ResolveField()` decorators to inform Nest that the decorated method is associated with a parent type (the `Author` type in our current example). Alternatively, instead of setting `@Resolver()` at the top of the class, this can be done for each method:
0 commit comments