Skip to content

Commit b7692fe

Browse files
committed
docs(graphql) - edits for mapped types, etc.
1 parent 7157e5f commit b7692fe

File tree

4 files changed

+56
-45
lines changed

4 files changed

+56
-45
lines changed

content/graphql/mapped-types.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
### Mapped types
22

3-
##### This chapter applies only to code first approach
3+
> warning **Warning** This chapter applies only to the code first approach.
44
5-
A common task is to take an existing type and make each of its properties optional. This happens often enough that NestJS provides a way to create new types based on old types — mapped types.
5+
As you build out features like CRUD (Create/Read/Update/Delete) it's often useful to construct variants on a base entity type. Nest provides several utility functions that perform type transformations to make this task more convenient.
66

7-
#### Partial
7+
#### Partial: PartialType() function
88

9-
Make all properties of a type optional.
9+
When building input validation types (also called DTOs), it's often useful to build **create** and **update** variations on the same type. For example, the **create** variant may require all fields, while the **update** variant may make all fields optional.
1010

11-
Original type:
11+
Nest provides the `PartialType()` utility function to make this task easier and minimize boilerplate.
12+
13+
The `PartialType()` function returns a type (class) with all the properties of the input type set to optional. For example, suppose we have a **create** type as follows:
1214

1315
```typescript
1416
@InputType()
@@ -24,7 +26,7 @@ class CreateUserInput {
2426
}
2527
```
2628

27-
Mapped type:
29+
By default, all of these fields are required. To create a type with the same fields, but with each one optional, use `PartialType()` passing the class reference (`CreateUserInput`) as an argument:
2830

2931
```typescript
3032
@InputType()
@@ -33,13 +35,16 @@ export class UpdateUserInput extends PartialType(CreateUserInput) {}
3335

3436
> info **Hint** The `PartialType()` function is imported from the `@nestjs/graphql` package.
3537
36-
> warning **Warning** If you want to create an `InputType` based on the `ObjectType`, you must explicitly specify it in the function, as follows: `PartialType(CreateUserInput, InputType)` where `InputType` is a reference to a decorator factory.
38+
The `PartialType()` function takes an optional second argument that is a reference to the decorator factory of the type being extended. In the example above, we are extending `CreateUserInput` which is annotated with the `@InputType()` decorator. We didn't need to pass `InputType` as the second argument since it's the default value. If you want to extend a class decorated with `@ObjectType`, pass `ObjectType` as the second argument. For example:
3739

38-
#### Pick
40+
```typescript
41+
@InputType()
42+
export class UpdateUserInput extends PartialType(User, ObjectType) {}
43+
```
3944

40-
Constructs a type by picking the set of properties from type.
45+
#### Pick: PickType() function
4146

42-
Original type:
47+
The `PickType()` function constructs a new type (class) by picking a set of properties from an input type. For example, suppose we start with a type like:
4348

4449
```typescript
4550
@InputType()
@@ -55,7 +60,7 @@ class CreateUserInput {
5560
}
5661
```
5762

58-
Mapped type:
63+
We can pick a set of properties from this class using the `PickType()` utility function:
5964

6065
```typescript
6166
@InputType()
@@ -64,11 +69,9 @@ export class UpdateEmailInput extends PickType(CreateUserInput, ['email']) {}
6469

6570
> info **Hint** The `PickType()` function is imported from the `@nestjs/graphql` package.
6671
67-
#### Pick
68-
69-
Constructs a type by picking all properties from type and then removing particular set of keys.
72+
#### Omit: OmitType() function
7073

71-
Original type:
74+
The `OmitType()` function constructs a type by picking all properties from an input type and then removing a particular set of keys. For example, suppose we start with a type like:
7275

7376
```typescript
7477
@InputType()
@@ -84,7 +87,7 @@ class CreateUserInput {
8487
}
8588
```
8689

87-
Mapped type:
90+
We can generate a derived type that has every property **except** `email` as shown below. In this construct, the second argument to `OmitType` is an array of property names.
8891

8992
```typescript
9093
@InputType()
@@ -95,13 +98,11 @@ export class UpdateUserInput extends OmitType(CreateUserInput, ['email']) {}
9598
9699
#### Composition
97100

98-
Mapped types are composeable.
99-
100-
Example:
101+
The type mapping utility functions are composable. For example, the following will produce a type (class) that has all of the properties of the `CreateUserInput` type except for `email`, and those properties will be set to optional:
101102

102103
```typescript
103104
@InputType()
104-
export class UpdateUserInput extends Partial(
105+
export class UpdateUserInput extends PartialType(
105106
OmitType(CreateUserInput, ['email']),
106107
) {}
107108
```

content/graphql/quick-start.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,13 @@ A fully working schema first sample is available [here](https://github.com/nestj
173173

174174
#### Accessing generated schema
175175

176-
To access the generated schema (in either code first or schema first approach), use the `GraphQLHost` class:
176+
To access the generated schema (in either the code first or schema first approach), use the `GraphQLSchemaHost` class:
177177

178178
```typescript
179179
const { schema } = app.get(GraphQLSchemaHost);
180180
```
181181

182-
> info **Hint** Make sure to call the `GraphQLSchemaHost#schema` getter when the application is already initialized (after the `onModuleInit` hook triggered by either `app.listen()` or `app.init()` method.
182+
> info **Hint** You must call the `GraphQLSchemaHost#schema` getter after the application has been initialized (after the `onModuleInit` hook has been triggered by either the `app.listen()` or `app.init()` method).
183183
184184
#### Async configuration
185185

content/graphql/resolvers-map.md

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -308,19 +308,23 @@ type Query {
308308

309309
#### Class inheritance
310310

311-
Args:
311+
You can use standard TypeScript class inheritance to create base classes with generic utility type features (fields and field properties, validations, etc.) that can be extended. For example, you may have a set of pagination related arguments that always include the standard `offset` and `limit` fields, but also other index fields that are type-specific. You can set up a class hierarchy as shown below.
312+
313+
Base `@ArgsType()` class:
312314

313315
```typescript
314316
@ArgsType()
315317
class PaginationArgs {
316-
@Field(type => Int)
318+
@Field((type) => Int)
317319
offset: number = 0;
318320

319-
@Field(type => Int)
321+
@Field((type) => Int)
320322
limit: number = 10;
321323
}
322324
```
323325

326+
Type specific sub-class of the base `@ArgsType()` class:
327+
324328
```typescript
325329
@ArgsType()
326330
class GetAuthorArgs extends PaginationArgs {
@@ -333,19 +337,21 @@ class GetAuthorArgs extends PaginationArgs {
333337
}
334338
```
335339

336-
Object types:
340+
The same approach can be taken with `@ObjectType()` objects. Define generic properties on the base class:
337341

338342
```typescript
339343
@ObjectType()
340344
class Character {
341-
@Field(type => Int)
345+
@Field((type) => Int)
342346
id: number;
343347

344348
@Field()
345349
name: string;
346350
}
347351
```
348352

353+
Add type-specific properties on sub-classes:
354+
349355
```typescript
350356
@ObjectType()
351357
class Warrior extends Character {
@@ -354,13 +360,13 @@ class Warrior extends Character {
354360
}
355361
```
356362

357-
Resolver inheritance (creating base resolver):
363+
You can use inheritance with a resolver as well. You can ensure type safety by combining inheritance and TypeScript generics. For example, to create a base class with a generic `findAll` query, use a construction like this:
358364

359365
```typescript
360366
function BaseResolver<T extends Type<unknown>>(classRef: T): any {
361367
@Resolver({ isAbstract: true })
362368
abstract class BaseResolverHost {
363-
@Query(type => [classRef], { name: `findAll${classRef.name}` })
369+
@Query((type) => [classRef], { name: `findAll${classRef.name}` })
364370
async findAll(): Promise<T[]> {
365371
return [];
366372
}
@@ -369,20 +375,24 @@ function BaseResolver<T extends Type<unknown>>(classRef: T): any {
369375
}
370376
```
371377

372-
- explicit return type `any` is required: otherwise TypeScript complains about the usage of private class definition (recommended: define an interface instead of `any`)
378+
Note the following:
379+
380+
- an explicit return type (`any` above) is required: otherwise TypeScript complains about the usage of a private class definition. Recommended: define an interface instead of using `any`.
373381
- `Type` is imported from the `@nestjs/common` package
374-
- `isAbstract: true` indicates that SDL shouldn't be generated for this class (you can set this for other types as well, e.g., object type)
382+
- The `isAbstract: true` property indicates that SDL (Schema Definition Language statements) shouldn't be generated for this class. Note, you can set this property for other types as well to suppress SDL generation.
383+
384+
Here's how you could generate a concrete sub-class of the `BaseResolver`:
375385

376386
```typescript
377-
@Resolver(of => Recipe)
387+
@Resolver((of) => Recipe)
378388
export class RecipesResolver extends BaseResolver(Recipe) {
379389
constructor(private recipesService: RecipesService) {
380390
super();
381391
}
382392
}
383393
```
384394

385-
Generated SDL:
395+
This construct would generated the following SDL:
386396

387397
```graphql
388398
type Query {
@@ -392,7 +402,7 @@ type Query {
392402

393403
#### Generics
394404

395-
Cursor-based pagination example:
405+
We saw one use of generics above. This powerful TypeScript feature can be used to create useful abstractions. For example, here's a sample cursor-based pagination implementation based on [this documentation](https://graphql.org/learn/pagination/#pagination-and-edges):
396406

397407
```typescript
398408
import { Field, ObjectType, Int } from '@nestjs/graphql';
@@ -401,22 +411,22 @@ import { Type } from '@nestjs/common';
401411
export function Paginated<T>(classRef: Type<T>) {
402412
@ObjectType(`${classRef.name}Edge`)
403413
abstract class EdgeType {
404-
@Field(type => String)
414+
@Field((type) => String)
405415
cursor: string;
406416

407-
@Field(type => classRef)
417+
@Field((type) => classRef)
408418
node: T;
409419
}
410420

411421
@ObjectType({ isAbstract: true })
412422
abstract class PaginatedType {
413-
@Field(type => [EdgeType], { nullable: true })
423+
@Field((type) => [EdgeType], { nullable: true })
414424
edges: EdgeType[];
415425

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

419-
@Field(type => Int)
429+
@Field((type) => Int)
420430
totalCount: number;
421431

422432
@Field()
@@ -426,7 +436,7 @@ export function Paginated<T>(classRef: Type<T>) {
426436
}
427437
```
428438

429-
No reason to explain everything in detail. We can link this doc: https://graphql.org/learn/pagination/#pagination-and-edges
439+
With the above base class defined, we can now easily create specialized types that inherit this behavior. For example:
430440

431441
```typescript
432442
@ObjectType()

content/graphql/schema-generator.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
### Generating SDL
22

3-
##### This chapter applies only to code first approach
3+
> warning **Warning** This chapter applies only to the code first approach.
44
5-
To manually generate (without running an application, connecting to the database, hooking up resolvers, etc.) a GraphQL SDL, use the `GraphQLSchemaBuilderModule`.
5+
To manually generate a GraphQL SDL schema (i.e., without running an application, connecting to the database, hooking up resolvers, etc.), use the `GraphQLSchemaBuilderModule`.
66

77
```typescript
88
async function generateSchema() {
@@ -15,9 +15,9 @@ async function generateSchema() {
1515
}
1616
```
1717

18-
> info **Hint** The `GraphQLSchemaBuilderModule` and `GraphQLSchemaFactory` are imported the `@nestjs/graphql` package, while the `printSchema` function is imported from the `graphql` package.
18+
> info **Hint** The `GraphQLSchemaBuilderModule` and `GraphQLSchemaFactory` are imported from the `@nestjs/graphql` package. The `printSchema` function is imported from the `graphql` package.
1919
20-
Multiple resolvers:
20+
The `gqlSchemaFactory.create()` method takes an array of resolver class references. For example:
2121

2222
```typescript
2323
const schema = await gqlSchemaFactory.create([
@@ -27,7 +27,7 @@ const schema = await gqlSchemaFactory.create([
2727
]);
2828
```
2929

30-
Passing options:
30+
It also allows several options:
3131

3232
```typescript
3333
const schema = await gqlSchemaFactory.create([RecipesResolver], {

0 commit comments

Comments
 (0)