Skip to content

Commit e783c17

Browse files
docs(): last updates
1 parent 3d1ffd1 commit e783c17

File tree

7 files changed

+145
-126
lines changed

7 files changed

+145
-126
lines changed

content/graphql/cli-plugin.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
### CLI Plugin
2+
3+
> warning **Warning** This chapter applies only to the code first approach.
4+
5+
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. However, some of these constraints can be addressed at compilation time. Nest provides a plugin that enhances the TypeScript compilation process to reduce the amount of boilerplate code required.
6+
7+
> info **Hint** This plugin is **opt-in**. If you prefer, you can declare all decorators manually, or only specific decorators where you need them.
8+
9+
#### Overview
10+
11+
The GraphQL plugin will automatically:
12+
13+
- annotate all input object, object type and args classes properties with `@Field` unless `@HideField` is used
14+
- set the `nullable` property depending on the question mark (e.g. `name?: string` will set `nullable: true`)
15+
- set the `type` property depending on the type (supports arrays as well)
16+
17+
Please, note that your filenames **must have** one of the following suffixes in order to be analyzed by the plugin: `['.input.ts', '.args.ts', '.entity.ts', '.model.ts']` (e.g., `author.entity.ts`). If you are using a different suffix, you can adjust the plugin's behavior by specifying the `typeFileNameSuffix` option (see below).
18+
19+
With what we've learned so far, you have 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:
20+
21+
```typescript
22+
@@filename(authors/models/author.model)
23+
@ObjectType()
24+
export class Author {
25+
@Field(type => Int)
26+
id: number;
27+
28+
@Field({ nullable: true })
29+
firstName?: string;
30+
31+
@Field({ nullable: true })
32+
lastName?: string;
33+
34+
@Field(type => [Post])
35+
posts: Post[];
36+
}
37+
```
38+
39+
While not a significant issue with medium-sized projects, it becomes verbose & hard to maintain once you have a large set of classes.
40+
41+
Now, with the GraphQL plugin enabled, the above class definition can be declared simply:
42+
43+
```typescript
44+
@@filename(authors/models/author.model)
45+
@ObjectType()
46+
export class Author {
47+
@Field(type => Int)
48+
id: number;
49+
firstName?: string;
50+
lastName?: string;
51+
posts: Post[];
52+
}
53+
```
54+
55+
The plugin adds appropriate decorators on-the-fly based on the **Abstract Syntax Tree**. Thus, you won't have to struggle with `@Field` decorators scattered throughout the code.
56+
57+
> info **Hint** The plugin will automatically generate any missing swagger properties, but if you need to override them, simply set them explicitly via `@Field()`.
58+
59+
To enable the plugin, open `nest-cli.json` (if you use [Nest CLI](/cli/overview)) and add the following `plugins` configuration:
60+
61+
```javascript
62+
{
63+
"collection": "@nestjs/schematics",
64+
"sourceRoot": "src",
65+
"compilerOptions": {
66+
"plugins": ["@nestjs/graphql/plugin"]
67+
}
68+
}
69+
```
70+
71+
You can use the `options` property to customize the behavior of the plugin.
72+
73+
```javascript
74+
"plugins": [
75+
{
76+
"name": "@nestjs/graphql/plugin",
77+
"options": {
78+
"typeFileNameSuffix": [".input.ts", ".args.ts"]
79+
}
80+
}
81+
]
82+
```
83+
84+
The `options` property has to fulfill the following interface:
85+
86+
```typescript
87+
export interface PluginOptions {
88+
typeFileNameSuffix?: string[];
89+
}
90+
```
91+
92+
<table>
93+
<tr>
94+
<th>Option</th>
95+
<th>Default</th>
96+
<th>Description</th>
97+
</tr>
98+
<tr>
99+
<td><code>typeFileNameSuffix</code></td>
100+
<td><code>['.input.ts', '.args.ts', '.entity.ts', '.model.ts']</code></td>
101+
<td>GraphQL types files suffix</td>
102+
</tr>
103+
</table>
104+
105+
If you don't use the CLI but instead have a custom `webpack` configuration, you can use this plugin in combination with `ts-loader`:
106+
107+
```javascript
108+
getCustomTransformers: (program: any) => ({
109+
before: [require('@nestjs/graphql/plugin').before({}, program)]
110+
}),
111+
```

content/graphql/mutations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class UpvotePostInput {
3939
}
4040
```
4141

42-
> info **Hint** The `@InputType()` decorator takes an options object as an argument, so you can, for example, specify the input type's description. Note that, due to TypeScript's metadata reflection system limitations, you must either use the `@Field` decorator to manually indicate a type, or use a [CLI plugin](/graphql/resolvers#cli-plugin).
42+
> info **Hint** The `@InputType()` decorator takes an options object as an argument, so you can, for example, specify the input type's description. Note that, due to TypeScript's metadata reflection system limitations, you must either use the `@Field` decorator to manually indicate a type, or use a [CLI plugin](/graphql/cli-plugin).
4343
4444
We can then use this type in the resolver class:
4545

content/graphql/resolvers-map.md

Lines changed: 11 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class Author {
4444
}
4545
```
4646

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.
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/cli-plugin) to generate these for us.
4848
4949
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.
5050

@@ -294,7 +294,7 @@ class GetAuthorArgs {
294294
}
295295
```
296296

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).
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/cli-plugin).
298298
299299
This will result in generating the following part of the GraphQL schema in SDL:
300300

@@ -315,10 +315,10 @@ Base `@ArgsType()` class:
315315
```typescript
316316
@ArgsType()
317317
class PaginationArgs {
318-
@Field(type => Int)
318+
@Field((type) => Int)
319319
offset: number = 0;
320320

321-
@Field(type => Int)
321+
@Field((type) => Int)
322322
limit: number = 10;
323323
}
324324
```
@@ -342,7 +342,7 @@ The same approach can be taken with `@ObjectType()` objects. Define generic prop
342342
```typescript
343343
@ObjectType()
344344
class Character {
345-
@Field(type => Int)
345+
@Field((type) => Int)
346346
id: number;
347347

348348
@Field()
@@ -366,7 +366,7 @@ You can use inheritance with a resolver as well. You can ensure type safety by c
366366
function BaseResolver<T extends Type<unknown>>(classRef: T): any {
367367
@Resolver({ isAbstract: true })
368368
abstract class BaseResolverHost {
369-
@Query(type => [classRef], { name: `findAll${classRef.name}` })
369+
@Query((type) => [classRef], { name: `findAll${classRef.name}` })
370370
async findAll(): Promise<T[]> {
371371
return [];
372372
}
@@ -411,22 +411,22 @@ import { Type } from '@nestjs/common';
411411
export function Paginated<T>(classRef: Type<T>) {
412412
@ObjectType(`${classRef.name}Edge`)
413413
abstract class EdgeType {
414-
@Field(type => String)
414+
@Field((type) => String)
415415
cursor: string;
416416

417-
@Field(type => classRef)
417+
@Field((type) => classRef)
418418
node: T;
419419
}
420420

421421
@ObjectType({ isAbstract: true })
422422
abstract class PaginatedType {
423-
@Field(type => [EdgeType], { nullable: true })
423+
@Field((type) => [EdgeType], { nullable: true })
424424
edges: EdgeType[];
425425

426-
@Field(type => [classRef], { nullable: true })
426+
@Field((type) => [classRef], { nullable: true })
427427
nodes: T[];
428428

429-
@Field(type => Int)
429+
@Field((type) => Int)
430430
totalCount: number;
431431

432432
@Field()
@@ -663,113 +663,3 @@ export class AuthorsModule {}
663663
```
664664

665665
> info **Hint** It is helpful to organize your code by your so-called **domain model** (similar to the way you would organize entry points in a REST API). In this approach, keep your models (`ObjectType` classes), resolvers and services together within a Nest module representing the domain model. Keep all of these components in a single folder per module. When you do this, and use the [Nest CLI](/cli/overview) to generate each element, Nest will wire all of these parts together (locating files in appropriate folders, generating entries in `provider` and `imports` arrays, etc.) automatically for you.
666-
667-
/////// MAYBE LET'S CREATE A SEPARATE CHAPTER(PAGE) FOR THIS? [CLI plugin]
668-
669-
#### CLI Plugin
670-
671-
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. However, some of these constraints can be addressed at compilation time. Nest provides a plugin that enhances the TypeScript compilation process to reduce the amount of boilerplate code required.
672-
673-
> warning **Hint** This plugin is **opt-in**. If you prefer, you can declare all decorators manually, or only specific decorators where you need them.
674-
675-
The GraphQL plugin will automatically:
676-
677-
- annotate all input object, object type and args classes properties with `@Field` unless `@HideField` is used
678-
- set the `nullable` property depending on the question mark (e.g. `name?: string` will set `nullable: true`)
679-
- set the `type` property depending on the type (supports arrays as well)
680-
681-
Please, note that your filenames **must have** one of the following suffixes in order to be analyzed by the plugin: `['.input.ts', '.args.ts', '.entity.ts', '.model.ts']` (e.g., `author.entity.ts`). If you are using a different suffix, you can adjust the plugin's behavior by specifying the `typeFileNameSuffix` option (see below).
682-
683-
With what we've learned so far, you have 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:
684-
685-
```typescript
686-
@@filename(authors/models/author.model)
687-
@ObjectType()
688-
export class Author {
689-
@Field(type => Int)
690-
id: number;
691-
692-
@Field({ nullable: true })
693-
firstName?: string;
694-
695-
@Field({ nullable: true })
696-
lastName?: string;
697-
698-
@Field(type => [Post])
699-
posts: Post[];
700-
}
701-
```
702-
703-
While not a significant issue with medium-sized projects, it becomes verbose & hard to maintain once you have a large set of classes.
704-
705-
Now, with the GraphQL plugin enabled, the above class definition can be declared simply:
706-
707-
```typescript
708-
@@filename(authors/models/author.model)
709-
@ObjectType()
710-
export class Author {
711-
@Field(type => Int)
712-
id: number;
713-
firstName?: string;
714-
lastName?: string;
715-
posts: Post[];
716-
}
717-
```
718-
719-
The plugin adds appropriate decorators on-the-fly based on the **Abstract Syntax Tree**. Thus, you won't have to struggle with `@Field` decorators scattered throughout the code.
720-
721-
> warning **Hint** The plugin will automatically generate any missing swagger properties, but if you need to override them, simply set them explicitly via `@Field()`.
722-
723-
To enable the plugin, open `nest-cli.json` (if you use [Nest CLI](/cli/overview)) and add the following `plugins` configuration:
724-
725-
```javascript
726-
{
727-
"collection": "@nestjs/schematics",
728-
"sourceRoot": "src",
729-
"compilerOptions": {
730-
"plugins": ["@nestjs/graphql/plugin"]
731-
}
732-
}
733-
```
734-
735-
You can use the `options` property to customize the behavior of the plugin.
736-
737-
```javascript
738-
"plugins": [
739-
{
740-
"name": "@nestjs/graphql/plugin",
741-
"options": {
742-
"typeFileNameSuffix": [".input.ts", ".args.ts"]
743-
}
744-
}
745-
]
746-
```
747-
748-
The `options` property has to fulfill the following interface:
749-
750-
```typescript
751-
export interface PluginOptions {
752-
typeFileNameSuffix?: string[];
753-
}
754-
```
755-
756-
<table>
757-
<tr>
758-
<th>Option</th>
759-
<th>Default</th>
760-
<th>Description</th>
761-
</tr>
762-
<tr>
763-
<td><code>typeFileNameSuffix</code></td>
764-
<td><code>['.input.ts', '.args.ts', '.entity.ts', '.model.ts']</code></td>
765-
<td>GraphQL types files suffix</td>
766-
</tr>
767-
</table>
768-
769-
If you don't use the CLI but instead have a custom `webpack` configuration, you can use this plugin in combination with `ts-loader`:
770-
771-
```javascript
772-
getCustomTransformers: (program: any) => ({
773-
before: [require('@nestjs/graphql/plugin').before({}, program)]
774-
}),
775-
```

content/graphql/schema-generator.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ async function generateSchema() {
1717

1818
> info **Hint** The `GraphQLSchemaBuilderModule` and `GraphQLSchemaFactory` are imported from the `@nestjs/graphql` package. The `printSchema` function is imported from the `graphql` package.
1919
20+
#### Usage
21+
2022
The `gqlSchemaFactory.create()` method takes an array of resolver class references. For example:
2123

2224
```typescript
@@ -37,5 +39,4 @@ const schema = await gqlSchemaFactory.create([RecipesResolver], {
3739
```
3840

3941
- `skipCheck`: ignore schema validation; boolean, defaults to `false`
40-
- `orphanedTypes`: list of classes that are not explicitly referenced (not part of the object graph) to be generated. Normally, if a class is declared but isn't otherwise referenced in the graph, it's omitted. The property value is an array of class references.
41-
42+
- `orphanedTypes`: list of classes that are not explicitly referenced (not part of the object graph) to be generated. Normally, if a class is declared but isn't otherwise referenced in the graph, it's omitted. The property value is an array of class references.

src/app/homepage/menu/menu.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export class MenuComponent implements OnInit {
114114
{ title: 'Unions', path: '/graphql/unions' },
115115
{ title: 'Enums', path: '/graphql/enums' },
116116
{ title: 'Mapped types', path: '/graphql/mapped-types' },
117+
{ title: 'CLI Plugin', path: '/graphql/cli-plugin' },
117118
{ title: 'Generating SDL', path: '/graphql/generating-sdl' },
118119
{
119120
title: 'Other features',
@@ -229,8 +230,8 @@ export class MenuComponent implements OnInit {
229230

230231
ngOnInit() {
231232
this.router.events
232-
.filter(event => event instanceof NavigationEnd)
233-
.subscribe(event => this.toggleCategory());
233+
.filter((event) => event instanceof NavigationEnd)
234+
.subscribe((event) => this.toggleCategory());
234235

235236
this.toggleCategory();
236237
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { BasePageComponent } from '../../page/page.component';
3+
4+
@Component({
5+
selector: 'app-cli-plugin',
6+
templateUrl: './cli-plugin.component.html',
7+
changeDetection: ChangeDetectionStrategy.OnPush,
8+
})
9+
export class CliPluginComponent extends BasePageComponent {}

src/app/homepage/pages/graphql/graphql.module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
22
import { NgModule } from '@angular/core';
33
import { RouterModule, Routes } from '@angular/router';
44
import { SharedModule } from '../../../shared/shared.module';
5+
import { CliPluginComponent } from './cli-plugin/cli-plugin.component';
56
import { DirectivesComponent } from './directives/directives.component';
67
import { EnumsComponent } from './enums/enums.component';
78
import { FederationComponent } from './federation/federation.component';
@@ -91,6 +92,11 @@ const routes: Routes = [
9192
component: MappedTypesComponent,
9293
data: { title: 'GraphQL + TypeScript - Mapped types' },
9394
},
95+
{
96+
path: 'cli-plugin',
97+
component: CliPluginComponent,
98+
data: { title: 'GraphQL + TypeScript - CLI Plugin' },
99+
},
94100
{
95101
path: 'generating-sdl',
96102
component: SchemaGeneratorComponent,
@@ -113,6 +119,7 @@ const routes: Routes = [
113119
ScalarsComponent,
114120
SchemaGeneratorComponent,
115121
MappedTypesComponent,
122+
CliPluginComponent,
116123
FederationComponent,
117124
],
118125
})

0 commit comments

Comments
 (0)