Skip to content

Commit a1a6710

Browse files
committed
docs(graphql): suggested improvements
1 parent 594bb66 commit a1a6710

File tree

2 files changed

+38
-19
lines changed

2 files changed

+38
-19
lines changed

content/graphql/quick-start.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ And depending on what you use (Express or Fastify), you need to install `apollo-
1616

1717
#### Overview
1818

19-
Nest offers two ways of building GraphQL applications, the **schema first** and the **code first** methods.
19+
Nest offers two ways of building GraphQL applications, the **code first** and the **schema first** methods.
2020

2121
In the **code first** approach, you use decorators and TypeScript classes to generate the corresponding GraphQL schema. This approach is useful if you prefer to work exclusively with TypeScript and avoid context switching between language syntaxes.
2222

@@ -36,7 +36,7 @@ import { GraphQLModule } from '@nestjs/graphql';
3636
GraphQLModule.forRoot({}),
3737
],
3838
})
39-
export class ApplicationModule {}
39+
export class AppModule {}
4040
```
4141

4242
The `forRoot()` method takes an options object as an argument. These options are passed through to the underlying Apollo instance (read more about available settings [here](https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server.html#constructor-options-lt-ApolloServer-gt)). For example, if you want to disable the `playground` and turn off `debug` mode, pass the following options:
@@ -54,16 +54,18 @@ import { GraphQLModule } from '@nestjs/graphql';
5454
}),
5555
],
5656
})
57-
export class ApplicationModule {}
57+
export class AppModule {}
5858
```
5959

6060
As mentioned, these options will be forwarded to the `ApolloServer` constructor.
6161

6262
<app-banner-enterprise></app-banner-enterprise>
6363

64-
#### Playground
64+
#### GraphQL playground
6565

66-
The playground is a graphical, interactive, in-browser GraphQL IDE, available by default on the same URL as the GraphQL server itself. With your application running in the background, open your web browser and navigate to `http://localhost:3000/graphql` (host and port may vary depending on your configuration). You should see the GraphQL playground, as shown below.
66+
The playground is a graphical, interactive, in-browser GraphQL IDE, available by default on the same URL as the GraphQL server itself. To see the playground running, you need a basic GraphQL server configured and running. To see it now, you can install the [working example here](https://github.com/nestjs/nest/tree/master/sample/12-graphql-schema-first). Alternatively, if you're following along with these code samples, once you've complete the steps in the [Resolvers chapter](/graphql/resolvers-map), you can access the playground.
67+
68+
With that in place, and with your application running in the background, open your web browser and navigate to `http://localhost:3000/graphql` (host and port may vary depending on your configuration). You should see the GraphQL playground, as shown below.
6769

6870
<figure>
6971
<img src="/assets/playground.png" alt="" />
@@ -111,9 +113,9 @@ GraphQLModule.forRoot({
111113
}),
112114
```
113115

114-
The `typePaths` property indicates where the `GraphQLModule` should look for GraphQL files. These files will be combined in memory; this allows you to split your schemas into several files and locate them near their resolvers.
116+
The `typePaths` property indicates where the `GraphQLModule` should look for GraphQL SDL schema definition files. These files will be combined in memory; this allows you to split your schemas into several files and locate them near their resolvers.
115117

116-
Creating GraphQL types and corresponding TypeScript definitions is redundant and tedious. It leaves us without a single source of truth -- each change made within SDL forces us to adjust interfaces as well. To address this, the `@nestjs/graphql` package can automatically generate TS definitions from the abstract syntax tree (AST). To enable this feature, add the `definitions` options property when configuring the `GraphQLModule`.
118+
You will typically need to have TypeScript definitions (classes and interfaces) that correspond to GraphQL types. Creating the corresponding TypeScript definitions is redundant and tedious. It leaves us without a single source of truth -- each change made within SDL forces us to adjust TypeScript definitions as well. To address this, the `@nestjs/graphql` package can automatically generate TypeScript definitions from the abstract syntax tree ([AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree)). To enable this feature, add the `definitions` options property when configuring the `GraphQLModule`.
117119

118120
```typescript
119121
GraphQLModule.forRoot({
@@ -124,7 +126,7 @@ GraphQLModule.forRoot({
124126
}),
125127
```
126128

127-
The path property of the `definitions` object (e.g., `src/graphql.ts` above) indicates where to save TypeScript output. By default, all types are generated as interfaces. To generate classes instead, specify the `outputAs` property with a value of `'class'`.
129+
The path property of the `definitions` object (e.g., `src/graphql.ts` above) indicates where to save generated TypeScript output. By default, all types are generated as interfaces. To generate classes instead, specify the `outputAs` property with a value of `'class'`.
128130

129131
```typescript
130132
GraphQLModule.forRoot({

content/graphql/resolvers-map.md

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
### Resolvers
22

3-
Resolvers provide the instructions for turning a [GraphQL](https://graphql.org/) operation (a query, mutation, or subscription) into data. They either return the same type of data we specify in our schema or a promise for that data. Typically, you create a resolvers map manually. The `@nestjs/graphql` package, on the other hand, generates a resolvers map automatically using the metadata provided by the decorators. To demonstrate the library basics, we'll create a simple authors API.
3+
Resolvers provide the instructions for turning a [GraphQL](https://graphql.org/) operation (a query, mutation, or subscription) into data. They return the same shape of data we specify in our schema -- either synchronously or as a promise that resolves to a result of that shape. Typically, you create a **resolvers map** manually. The `@nestjs/graphql` package, on the other hand, generates a resolvers map automatically using the metadata provided by the decorators. To demonstrate the library basics, we'll create a simple authors API.
44

55
#### Code first
66

@@ -214,7 +214,7 @@ You may also notice that such classes play very well with the `ValidationPipe` (
214214

215215
#### Schema first
216216

217-
As mentioned in the [previous](/graphql/quick-start) chapter, in the schema first approach we manually define our types in SDL (read [more](http://graphql.org/learn/schema/#type-language)).
217+
As mentioned in the [previous](/graphql/quick-start) chapter, in the schema first approach we manually define our types in SDL (read [more](http://graphql.org/learn/schema/#type-language)). Consider the following type definitions:
218218

219219
```graphql
220220
type Author {
@@ -235,7 +235,7 @@ type Query {
235235
}
236236
```
237237

238-
Our GraphQL schema contains a single exposed query - `author(id: Int!): Author`. Now, let's create an `AuthorResolver` class.
238+
This schema exposes a single query - `author(id: Int!): Author`. Let's create an `AuthorResolver` class that resolves author queries:
239239

240240
```typescript
241241
@Resolver('Author')
@@ -260,9 +260,9 @@ export class AuthorResolver {
260260

261261
> info **Hint** All decorators (e.g., `@Resolver`, `@ResolveField`, `@Args`, etc.) are exported from the `@nestjs/graphql` package.
262262
263-
> 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 resolvers can interact with other providers.
263+
> 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.
264264
265-
The `@Resolver()` decorator does not affect queries and mutations (neither `@Query()` nor `@Mutation()` decorators). It only informs Nest that each `@ResolveField()` inside this particular class has a parent, which is an `Author` type in this case (`Author.posts` relation). Basically, instead of setting `@Resolver()` at the top of the class, this can be done close to the method:
265+
The `@Resolver()` decorator is used to inform Nest that each `@ResolveField()` method inside the class has a parent (the `Author` type in this case). The `@Resolver()` decorator **does not** affect queries (`@Query()` decorator) or mutations (`@Mutation()` decorator). Alternatively, instead of setting `@Resolver()` at the top of the class, this can be done for each method:
266266

267267
```typescript
268268
@Resolver('Author')
@@ -273,11 +273,28 @@ async posts(@Parent() author) {
273273
}
274274
```
275275

276-
> warning **Warning** Using the `@Resolver` decorator at the method-level is not supported with the **code first** approach.
276+
In this case (`@Resolver()` decorator at the method level), if you have multiple `@ResolveField()` decorators inside a class, you must add `@Resolver()` to all of them. This is not considered the best practice (as it creates extra overhead).
277277

278-
However, if you have multiple `@ResolveField()` decorators inside one class, you must add `@Resolver()` to all of them, which is not necessarily a good practice (as it creates extra overhead).
278+
> warning **Warning** Using the `@Resolver` decorator at the method level is not supported with the **code first** approach.
279279
280-
Conventionally, we would use something like `getAuthor()` or `getPosts()` as method names. We can easily do this by passing the real names as arguments of the decorator.
280+
In the above examples, the `@Query()` and `@ResolveField()` decorators are associated with the types based on the method name. For example, consider the following construction from the example above:
281+
282+
```typescript
283+
@Query()
284+
async author(@Args('id') id: number) {
285+
return this.authorsService.findOneById(id);
286+
}
287+
```
288+
289+
This generates the resolver map entry for the author query in our schema (because the method name `author` matches the `author` field in the `Query` type):
290+
291+
```graphql
292+
type Query {
293+
author(id: Int!): Author
294+
}
295+
```
296+
297+
Conventionally, we would prefer to decouple these, using names like `getAuthor()` or `getPosts()` for our resolver methods. We can easily do this by passing the mapping names as arguments of the decorator, as shown below
281298

282299
```typescript
283300
@Resolver('Author')
@@ -323,7 +340,7 @@ export abstract class IQuery {
323340
}
324341
```
325342

326-
Generating classes (instead of interfaces) allows you to use **decorators** in combination with the schema first approach, which makes them extremely useful for validation purposes (read [more](/techniques/validation)). For example:
343+
Generating classes (instead of the default technique of generating interfaces) allows you to use declarative validation **decorators** in combination with the schema first approach, which is an extremely useful technique (read [more](/techniques/validation)). For example, you could add these `class-validator` decorators to the generated `CreatePostInput` class as shown below:
327344

328345
```typescript
329346
import { MinLength, MaxLength } from 'class-validator';
@@ -386,7 +403,7 @@ These arguments have the following meanings:
386403

387404
#### Module
388405

389-
Once we're done with the above steps, we have to provide the `AuthorResolver` somewhere (for dependency injection). For example, we can do this in the newly created `AuthorsModule`.
406+
Once we're done with the above steps, we have to provide the `AuthorResolver` somewhere (list it as a `provider` in some module, for dependency injection). For example, we can do this in an `AuthorsModule`, which can also provide other services needed in this context.
390407

391408
```typescript
392409
@Module({
@@ -412,7 +429,7 @@ The GraphQL plugin will automatically:
412429
- set the `nullable` property depending on the question mark (e.g. `name?: string` will set `nullable: true`)
413430
- set the `type` property depending on the type (supports arrays as well)
414431

415-
Please, note that your filenames **must have** one of the following suffixes: `['.input.ts', '.args.ts', '.entity.ts']` (e.g., `author.entity.ts`) in order to be analysed by the plugin.
432+
Please, note that your filenames **must have** one of the following suffixes: `['.input.ts', '.args.ts', '.entity.ts']` (e.g., `author.entity.ts`) in order to be analyzed by the plugin.
416433

417434
Previously, you had to duplicate a lot of code to let the package know how your type should be declared in GraphQL. For example, you could define a simple `Author` class as follows:
418435

0 commit comments

Comments
 (0)