## 解析器
解析器提供將 `GraphQL` 操作(查詢、突變或訂閱)轉換為數據的說明。 它們返回我們在模式中指定的相同形狀的數據——同步或作為解析為該形狀結果的承諾。 通常,您手動創建解析器映射。 另一方面,`@nestjs/graphql` 包使用用于注釋類的裝飾器提供的元數據自動生成解析器映射。 為了演示使用包功能創建 `GraphQL API` 的過程,我們將創建一個簡單的作者 API。
### ** 代碼優先 **
在代碼優先的方法中,我們不遵循通過手動編寫 `GraphQL SDL` 創建 `GraphQL` 模式的典型過程。 相反,我們使用 `TypeScript` 裝飾器從 `TypeScript `類定義生成 SDL。 `@nestjs/graphql` 包讀取通過裝飾器定義的元數據并自動為您生成模式。
### 對象類型[#](#object-types)
GraphQL 模式中的大多數定義都是**對象類型**。您定義的每個對象類型都應該代表一個應用程序客戶端可能需要與之交互的域對象。例如,我們的示例 API 需要能夠獲取作者列表及其帖子,因此我們應該定義`Author`類型和`Post`類型以支持此功能。
如果我們使用模式優先的方法,我們將使用 SDL 定義這樣的模式,如下所示:
~~~graphql
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post!]!
}
~~~
在代碼優先方式中,我們不必手動編寫SDL。相反,我們只需使用裝飾器。
```typescript
import { Field, Int, ObjectType } from 'type-graphql';
import { Post } from './post';
@ObjectType()
export class Author {
@Field(type => Int)
id: number;
@Field({ nullable: true })
firstName?: string;
@Field({ nullable: true })
lastName?: string;
@Field(type => [Post])
posts: Post[];
}
```
>TypeScript 的元數據反射系統有幾個限制,例如,無法確定類包含哪些屬性或識別給定屬性是可選的還是必需的。 由于這些限制,我們必須在架構定義類中顯式使用 `@Field() `裝飾器來提供有關每個字段的 GraphQL 類型和可選性的元數據,或者使用 [CLI 插件](https://docs.nestjs.com/graphql/cli-plugin)為我們生成這些。
Author 對象類型與任何類一樣,由一組字段組成,每個字段都聲明一個類型。 字段的類型對應于 GraphQL 類型。 字段的 GraphQL 類型可以是其他對象類型或標量類型。 GraphQL 標量類型是解析為單個值的原語(如 `ID`、`String`、`Boolean` 或 `Int`)。
>除了 GraphQL 的內置標量類型之外,您還可以定義自定義標量類型([閱讀更多](https://docs.nestjs.com/graphql/scalars))。
上面的 Author 對象類型定義將導致 Nest 生成我們上面展示的 SDL:
~~~graphql
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post!]!
}
~~~
`@Field() `裝飾器接受可選的類型函數(例如,type => Int)和可選的選項對象。
當 TypeScript 類型系統和 GraphQL 類型系統之間可能存在歧義時,需要 type 函數。 具體來說:字符串和布爾類型不需要; 它是數字所必需的(必須映射到 GraphQL Int 或 Float)。 type 函數應該簡單地返回所需的 GraphQL 類型(如這些章節中的各種示例所示)。
選項對象可以具有以下任何鍵/值對:
* `nullable`: 用于指定一個字段是否可以為空(在 SDL 中,每個字段默認都是不可為空的);`boolean`
* `description`:用于設置字段描述;`string`
* `deprecationReason`:用于將字段標記為已棄用;`string`
例如:
~~~typescript
@Field({ description: `Book title`, deprecationReason: 'Not useful in v2 schema' })
title: string;
~~~
>您還可以為整個對象類型添加描述或棄用:`@ObjectType({ description: 'Author model' })`.
當字段為數組時,我們必須在`Field()`裝飾器的type函數中手動指明數組類型,如下圖:
~~~typescript
@Field(type => [Post])
posts: Post[];
~~~
> **提示**使用數組括號表示法 (`[ ]`),我們可以指示數組的深度。例如, 使用`[[Int]]`將表示一個整數矩陣。
要聲明數組的項(不是數組本身)可以為空,請將`nullable`屬性設置`'items'`為如下所示:
~~~typescript
@Field(type => [Post], { nullable: 'items' })
posts: Post[];
~~~
> **提示**如果數組及其項都可以為空,則改為設置`nullable`為`'itemsAndList'`。
Author 模型已創建。現在,讓我們創建缺少的 Post 類。
```typescript
import { Field, Int, ObjectType } from 'type-graphql';
@ObjectType()
export class Post {
@Field(type => Int)
id: number;
@Field()
title: string;
@Field(type => Int, { nullable: true })
votes?: number;
}
```
Post 對象類型將導致在 SDL 中生成 GraphQL 模式的以下部分:
~~~graphql
type Post {
id: Int!
title: String!
votes: Int
}
~~~
####代碼優先解析器[#](#code-first-resolver)
至此,我們已經定義了可以存在于數據圖中的對象(類型定義),但是客戶端還沒有辦法與這些對象進行交互。為了解決這個問題,我們需要創建一個解析器類。在代碼優先方法中,解析器類既定義解析器函數**又**生成**查詢類型**。當我們通過以下示例進行操作時,這一點將很清楚:
```typescript
@Resolver(of => Author)
export class AuthorResolver {
constructor(
private readonly authorsService: AuthorsService,
private readonly postsService: PostsService,
) {}
@Query(returns => Author)
async author(@Args({ name: 'id', type: () => Int }) id: number) {
return await this.authorsService.findOneById(id);
}
@ResolveProperty()
async posts(@Parent() author) {
const { id } = author;
return await this.postsService.findAll({ authorId: id });
}
}
```
>所有裝飾器(例如,`@Resolver`、`@ResolveField`、`@Args` 等)都從 `@nestjs/graphql` 包中導出。
您可以定義多個解析器類。Nest 將在運行時組合這些。有關代碼組織的更多信息,請參閱下面的[模塊部分。](https://docs.nestjs.com/graphql/resolvers#module)
>`AuthorsService `和 `PostsService` 類中的邏輯可以根據需要簡單或復雜。 這個例子的主要目的是展示如何構建解析器以及它們如何與其他提供者交互。
在上面的示例中,我們創建了 `AuthorsResolver`,它定義了一個查詢解析器函數和一個字段解析器函數。為了創建解析器,我們創建了一個使用解析器函數作為方法的類,并使用 `@Resolver()` 裝飾器注釋該類。
在此示例中,我們定義了一個查詢處理程序,以根據請求中發送的 `id` 獲取作者對象。要指定該方法是一個查詢處理程序,請使用 `@Query() `裝飾器。
傳遞給 `@Resolver()` 裝飾器的參數是可選的,但當我們的圖形變得不平凡時就會發揮作用。它用于提供字段解析器函數在遍歷對象圖時使用的父對象。
在我們的示例中,由于該類包含一個字段解析器函數(對于 `Author` 對象類型的 posts 屬性),我們必須為 `@Resolver()` 裝飾器提供一個值來指示哪個類是父類型(即對應的 `ObjectType`類名)用于此類中定義的所有字段解析器。從示例中應該可以清楚地看出,在編寫字段解析器函數時,有必要訪問父對象(被解析的字段所屬的對象)。在此示例中,我們使用字段解析器填充作者的帖子數組,該字段解析器調用以作者 ID 作為參數的服務。因此需要在 `@Resolver()`裝飾器中識別父對象。注意 `@Parent() `方法參數裝飾器的相應使用,然后在字段解析器中提取對該父對象的引用。
我們可以定義多個 `@Query()` 解析器函數(在此類中以及在任何其他解析器類中),它們將與解析器映射中的相應條目一起在生成的 SDL 中聚合到單個 Query 類型定義中。這允許您定義接近他們使用的模型和服務的查詢,并在模塊中保持良好的組織。
>Nest CLI 提供了一個生成器(原理圖),它會自動生成所有樣板代碼,以幫助我們避免所有這些,并使開發人員的體驗更加簡單。 在[此處](https://docs.nestjs.com/recipes/crud-generator)閱讀有關此功能的更多信息。
### 查詢類型名稱[#](#query-type-names)
在上面的示例中,`@Query()`裝飾器根據方法名稱生成 GraphQL 模式查詢類型名稱。例如,考慮上面示例中的以下構造:
~~~typescript
@Query(returns => Author)
async author(@Args('id', { type: () => Int }) id: number) {
return this.authorsService.findOneById(id);
}
~~~
這會在我們的架構中為作者查詢生成以下條目(查詢類型使用與方法名稱相同的名稱):
~~~graphql
type Query {
author(id: Int!): Author
}
~~~
> **提示**[在此處](https://graphql.org/learn/queries/)了解有關 GraphQL 查詢的更多信息。
按照慣例,我們更喜歡將這些名稱解耦;例如,我們更喜歡`getAuthor()`為我們的查詢處理方法使用一個名稱,但仍然使用`author`我們的查詢類型名稱。這同樣適用于我們的字段解析器。我們可以通過將映射名稱作為裝飾器`@Query()`和`@ResolveField()`裝飾器的參數傳遞來輕松做到這一點,如下所示:
```typescript
@Resolver(of => Author)
export class AuthorResolver {
constructor(
private readonly authorsService: AuthorsService,
private readonly postsService: PostsService,
) {}
@Query(returns => Author, { name: 'author' })
async getAuthor(@Args({ name: 'id', type: () => Int }) id: number) {
return await this.authorsService.findOneById(id);
}
@ResolveProperty('posts')
async getPosts(@Parent() author) {
const { id } = author;
return await this.postsService.findAll({ authorId: id });
}
}
```
上面的`getAuthor`處理程序方法將導致在 SDL 中生成 GraphQL 模式的以下部分:
~~~graphql
type Query {
author(id: Int!): Author
}
~~~
### 查詢裝飾器選項[#](#query-decorator-options)
`@Query()` 裝飾器的選項對象(我們在上面傳遞 `{name: 'author'} )`接受許多鍵/值對:
- name:查詢的名稱; 一個字符串
- description:將用于生成 GraphQL 模式文檔的描述(例如,在 GraphQL 游樂場中); 一個字符串
- deprecationReason:設置查詢元數據以將查詢顯示為已棄用(例如,在 GraphQL 游樂場中); 一個字符串
- nullable:查詢是否可以返回空數據響應; boolean 或 'items' 或 'itemsAndList' (有關 'items' 和 'itemsAndList' 的詳細信息,請參見上文)
#### args 裝飾器選項[#](#args-decorator-options)
使用`@Args()`裝飾器從請求中提取參數以在方法處理程序中使用。[這與REST 路由參數參數提取](https://docs.nestjs.com/controllers#route-parameters)非常相似。
通常你的`@Args()`裝飾器會很簡單,并且不需要像`getAuthor()`上面方法中看到的對象參數。例如,如果標識符的類型是字符串,則以下構造就足夠了,并且只需從入站 GraphQL 請求中提取命名字段以用作方法參數。
```typescript
@Args('id') id: string
```
在 getAuthor() 的情況下,使用數字類型,這是一個挑戰。 數字 `TypeScript` 類型沒有為我們提供有關預期 GraphQL 表示的足夠信息(例如,`Int` 與 `Float`)。 因此我們必須顯式地傳遞類型引用。 我們通過將第二個參數傳遞給 `Args() `裝飾器來做到這一點,其中包含參數選項,如下所示:
~~~typescript
@Query(returns => Author, { name: 'author' })
async getAuthor(@Args('id', { type: () => Int }) id: number) {
return this.authorsService.findOneById(id);
}
~~~
options 對象允許我們指定以下可選的鍵值對:
- `type`: 返回 GraphQL 類型的函數
- `defaultValue`:默認值,`any`類型
- `description`: 描述元數據 ;`string`
- `deprecationReason`: 棄用字段并提供描述原因的元數據;`string`
- `nullable`:字段是否可以為空
查詢處理程序方法可以采用多個參數。 假設我們想根據名字和姓氏來獲取作者。 在這種情況下,我們可以調用`@Args` 兩次:
~~~typescript
getAuthor(
@Args('firstName', { nullable: true }) firstName?: string,
@Args('lastName', { defaultValue: '' }) lastName?: string,
) {}
~~~
### 專用參數類[#](#dedicated-arguments-class)
使用內聯`@Args()`調用,上述示例的代碼會變得臃腫。相反,您可以創建一個專用的`GetAuthorArgs`參數類并在處理程序方法中訪問它,如下所示:
```typescript
@Args() id: AuthorArgs
```
使用 `@ArgsType()` 創建 `GetAuthorArgs` 類,如下所示:
>authors/dto/get-author.args.ts
~~~typescript
import { MinLength } from 'class-validator';
import { Field, ArgsType } from '@nestjs/graphql';
@ArgsType()
class GetAuthorArgs {
@Field({ nullable: true })
firstName?: string;
@Field({ defaultValue: '' })
@MinLength(3)
lastName: string;
}
~~~
>同樣,由于 TypeScript 的元數據反射系統限制,需要使用 `@Field `裝飾器手動指示類型和可選性,或者使用 CLI 插件。
這將導致在 SDL 中生成 GraphQL 架構的以下部分:
~~~graphql
type Query {
author(firstName: String, lastName: String = ''): Author
}
~~~
>請注意,像 `GetAuthorArgs` 這樣的參數類與 `ValidationPipe` 配合得很好([閱讀更多](https://docs.nestjs.com/techniques/validation))。
### 類繼承
您可以使用標準 TypeScript 類繼承來創建具有可擴展的通用實用程序類型功能(字段和字段屬性、驗證等)的基類。 例如,您可能有一組與分頁相關的參數,這些參數始終包括標準偏移量和限制字段,還包括其他特定于類型的索引字段。 您可以設置類層次結構,如下所示。
`@ArgsType()` 基類:
~~~typescript
@ArgsType()
class PaginationArgs {
@Field((type) => Int)
offset: number = 0;
@Field((type) => Int)
limit: number = 10;
}
~~~
基類 `@ArgsType() `的類型特定子類:
~~~typescript
@ArgsType()
class GetAuthorArgs extends PaginationArgs {
@Field({ nullable: true })
firstName?: string;
@Field({ defaultValue: '' })
@MinLength(3)
lastName: string;
}
~~~
可以對`@ObjectType()`對象采取相同的方法。 在基類上定義通用屬性:
~~~typescript
@ObjectType()
class Character {
@Field((type) => Int)
id: number;
@Field()
name: string;
}
~~~
在子類上添加特定于類型的屬性:
~~~typescript
@ObjectType()
class Warrior extends Character {
@Field()
level: number;
}
~~~
您也可以將繼承與解析器一起使用。 您可以通過結合繼承和 TypeScript 泛型來確保類型安全。 例如,要使用通用 `findAll` 查詢創建基類,請使用如下結構:
~~~typescript
function BaseResolver<T extends Type<unknown>>(classRef: T): any {
@Resolver({ isAbstract: true })
abstract class BaseResolverHost {
@Query((type) => [classRef], { name: `findAll${classRef.name}` })
async findAll(): Promise<T[]> {
return [];
}
}
return BaseResolverHost;
}
~~~
請注意以下事項:
- 需要一個顯式的返回類型(以上任何一種):否則 TypeScript 會抱怨使用了私有類定義。 推薦:定義一個接口而不是使用`any`。
- `Type`是從 `@nestjs/common` 包中導入的
- `isAbstract: true `屬性指示不應為此類生成 SDL(架構定義語言語句)。 請注意,您也可以為其他類型設置此屬性以抑制 SDL 生成。
以下是生成 BaseResolver 的具體子類的方法:
~~~typescript
@Resolver((of) => Recipe)
export class RecipesResolver extends BaseResolver(Recipe) {
constructor(private recipesService: RecipesService) {
super();
}
}
~~~
此構造將生成以下 SDL:
~~~graphql
type Query {
findAllRecipe: [Recipe!]!
}
~~~
### 泛型
我們在上面看到了泛型的一種用法。 這個強大的 TypeScript 功能可用于創建有用的抽象。 例如,這是一個基于此文檔的基于光標的分頁實現示例:
~~~typescript
import { Field, ObjectType, Int } from '@nestjs/graphql';
import { Type } from '@nestjs/common';
interface IEdgeType<T> {
cursor: string;
node: T;
}
export interface IPaginatedType<T> {
edges: IEdgeType<T>[];
nodes: T[];
totalCount: number;
hasNextPage: boolean;
}
export function Paginated<T>(classRef: Type<T>): Type<IPaginatedType<T>> {
@ObjectType(`${classRef.name}Edge`)
abstract class EdgeType {
@Field((type) => String)
cursor: string;
@Field((type) => classRef)
node: T;
}
@ObjectType({ isAbstract: true })
abstract class PaginatedType implements IPaginatedType<T> {
@Field((type) => [EdgeType], { nullable: true })
edges: EdgeType[];
@Field((type) => [classRef], { nullable: true })
nodes: T[];
@Field((type) => Int)
totalCount: number;
@Field()
hasNextPage: boolean;
}
return PaginatedType as Type<IPaginatedType<T>>;
}
~~~
定義了上述基類后,我們現在可以輕松創建繼承此行為的專用類型。 例如:
~~~typescript
@ObjectType()
class PaginatedAuthor extends Paginated(Author) {}
~~~
#### ** 架構優先 **
正如提到[以前的章節](https://docs.nestjs.com/graphql/quick-start),讓我們在 SDL 中定義我們的類型(閱讀[更多](http://graphql.cn/learn/schema/#type-language)):
>為方便本章,我們將所有 SDL 聚合在一個位置(例如,一個 `.graphql` 文件,如下所示)。 在實踐中,您可能會發現以模塊化方式組織代碼是合適的。 例如,在該實體的專用目錄中創建具有表示每個域實體的類型定義的單個 SDL 文件以及相關服務、解析器代碼和 Nest 模塊定義類會很有幫助。 Nest 將在運行時聚合所有單獨的模式類型定義。
```graphql
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int!
title: String!
votes: Int
}
type Query {
author(id: Int!): Author
}
```
架構優先解析器[#](#schema-first-resolver)
我們的 GraphQL 架構包含公開的單個查詢 `author(id: Int!): Author` 。
> [在此處](https://graphql.org/learn/queries/)了解有關 `GraphQL` 查詢的更多信息。
現在,讓我們創建一個 `AuthorResolver` 。
```graphql
@Resolver('Author')
export class AuthorResolver {
constructor(
private readonly authorsService: AuthorsService,
private readonly postsService: PostsService,
) {}
@Query()
async author(@Args('id') id: number) {
return await this.authorsService.findOneById(id);
}
@ResolveProperty()
async posts(@Parent() author) {
const { id } = author;
return await this.postsService.findAll({ authorId: id });
}
}
```
>所有裝飾器(例如,`@Resolver`、`@ResolveField`、`@Args` 等)都從 `@nestjs/graphql` 包中導出。
> 提示:使用 `@Resolver()` 裝飾器則不必將類標記為 `@Injectable()` ,否則必須這么做。
`@Resolver()` 裝飾器不影響查詢和對象變動 (`@Query()` 和 `@Mutation()` 裝飾器)。這只會通知 Nest, 每個 `@ResolveProperty()` 有一個父節點, `Author` 在這種情況下是父節點, Author在這種情況下是一個類型(Author.posts 關系)。基本上,不是為類設置 @Resolver() ,而是為函數:
```typescript
@Resolver('Author')
@ResolveProperty()
async posts(@Parent() author) {
const { id } = author;
return await this.postsService.findAll({ authorId: id });
}
```
但當 @ResolveProperty() 在一個類中有多個,則必須為所有的都添加 @Resolver(),這不是一個好習慣(額外的開銷)。
> 任何傳遞給 @Resolver() 的類名參數都不會影響查詢(`@Query()` 裝飾器)或突變(`@Mutation()` 裝飾器)。
>代碼優先方法不支持在方法級別使用 `@Resolver `裝飾器。
在上面的示例中,`@Query()` 和 `@ResolveField() `裝飾器根據方法名稱與 `GraphQL` 模式類型相關聯。 例如,考慮上面示例中的以下構造:
~~~typescript
@Query()
async author(@Args('id') id: number) {
return this.authorsService.findOneById(id);
}
~~~
這會在我們的架構中為作者查詢生成以下條目(查詢類型使用與方法名稱相同的名稱):
~~~graphql
type Query {
author(id: Int!): Author
}
~~~
按照慣例,我們更愿意將它們解耦,使用諸如 getAuthor() 或 getPosts() 之類的名稱作為解析器方法。 我們可以通過將映射名稱作為參數傳遞給裝飾器來輕松做到這一點,如下所示:
```typescript
@Resolver('Author')
export class AuthorResolver {
constructor(
private readonly authorsService: AuthorsService,
private readonly postsService: PostsService,
) {}
@Query('author')
async getAuthor(@Args('id') id: number) {
return await this.authorsService.findOneById(id);
}
@ResolveProperty('posts')
async getPosts(@Parent() author) {
const { id } = author;
return await this.postsService.findAll({ authorId: id });
}
}
```
> 這個 `@Resolver()` 裝飾器可以在函數級別被使用。
> Nest CLI 提供了一個生成器(原理圖),它會自動生成所有樣板代碼,以幫助我們避免所有這些,并使開發人員的體驗更加簡單。 在[此處](https://docs.nestjs.com/recipes/crud-generator)閱讀有關此功能的更多信息。
### 類型生產(Typings)
假設我們使用模式優先的方法并啟用了類型生成功能(使用 `outputAs: 'class' `如[上一章](https://docs.nestjs.com/graphql/quick-start)所示),一旦您運行應用程序,它將生成以下文件(在您在 `GraphQLModule` 中指定的位置 `.forRoot()` 方法)。 例如,在 `src/graphql.ts` 中:
```typescript
export class Author {
id: number;
firstName?: string;
lastName?: string;
posts?: Post[];
}
export class Post {
id: number;
title: string;
votes?: number;
}
export abstract class IQuery {
abstract author(id: number): Author | Promise<Author>;
}
```
通過生成類(而不是生成接口的默認技術),您可以將聲明性驗證裝飾器與模式優先方法結合使用,這是一種非常有用的技術([閱讀更多](https://docs.nestjs.com/techniques/validation))。 例如,您可以將類驗證器裝飾器添加到生成的 `CreatePostInput` 類,如下所示,以在標題字段上強制最小和最大字符串長度:
```typescript
import { MinLength, MaxLength } from 'class-validator';
export class CreatePostInput {
@MinLength(3)
@MaxLength(50)
title: string;
}
```
>要啟用輸入(和參數)的自動驗證,請使用 `ValidationPipe`。 在[此處](https://docs.nestjs.com/techniques/validation)閱讀有關驗證的更多信息,更具體地在[此處](https://docs.nestjs.com/pipes)閱讀有關管道的信息。
盡管如此,如果將裝飾器直接添加到自動生成的文件中,它們將在每次連續更改時被丟棄。因此,您應該創建一個單獨的文件,并簡單地擴展生成的類。
```typescript
import { MinLength, MaxLength } from 'class-validator';
import { Post } from '../../graphql.ts';
export class CreatePostInput extends Post {
@MinLength(3)
@MaxLength(50)
title: string;
}
```
### GraphQL 參數裝飾器[#](#graphql-argument-decorators)
在上面的示例中,您可能會注意到我們使用專用裝飾器來引用以下參數。下面是提供的裝飾器和它們代表的普通 Apollo 參數的比較。
|||
|---|---|
| `@Root()` 和 `@Parent()` | `root`/`parent` |
| `@Context(param?:string)` | `context`/`context[param]` |
| `@Info(param?:string)` | `info`/`info[param]` |
| `@Args(param?:string)` | `args`/`args[param]` |
這些參數具有以下含義:
* `root`: 一個對象,包含從父字段的解析器返回的結果,或者在頂級`Query`字段的情況下,`rootValue`從服務器配置傳遞。
* `context`:在特定查詢中由所有解析器共享的對象;通常用于包含每個請求的狀態。
* `info`:一個對象,其中包含有關查詢執行狀態的信息。
* `args`: 一個對象,其參數傳遞到查詢中的字段中。
### 模塊
完成上述步驟后,我們已經以聲明方式指定了 `GraphQLModule` 生成解析器映射所需的所有信息。 `GraphQLModule` 使用反射來內省通過裝飾器提供的元數據,并自動將類轉換為正確的解析器映射。
您需要注意的唯一另一件事是提供(即,作為某個模塊中的提供者列出)解析器類(`AuthorsResolver`),并在某處導入模塊(`AuthorsModule`),因此Nest 將能夠利用 它。
例如,我們可以在 `AuthorsModule` 中執行此操作,該模塊還可以提供此上下文所需的其他服務。 確保在某處導入 `AuthorsModule`(例如,在根模塊中,或由根模塊導入的其他模塊中)。
```typescript
@Module({
imports: [PostsModule],
providers: [AuthorsService, AuthorResolver],
})
export class AuthorsModule {}
```
該 `GraphQLModule` 會考慮反映了元數據和轉化類到正確的解析器的自動映射。您應該注意的是您需要在某處 import 此模塊,Nest 才會知道 `AuthorsModule` 確實存在。
> 通過所謂的域模型來組織代碼會很有幫助(類似于在 `REST API `中組織入口點的方式)。 在這種方法中,將您的模型(`ObjectType` 類)、解析器和服務放在代表領域模型的 Nest 模塊中。 將所有這些組件保存在每個模塊的單個文件夾中。 當您執行此操作并使用 Nest CLI 生成每個元素時,Nest 會自動為您將所有這些部分連接在一起(在適當的文件夾中定位文件,在提供程序中生成條目并導入數組等)。
> 提示:在[此處](http://graphql.cn/learn/queries/)了解有關 GraphQL 查詢的更多信息。
- 介紹
- 概述
- 第一步
- 控制器
- 提供者
- 模塊
- 中間件
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 自定義裝飾器
- 基礎知識
- 自定義提供者
- 異步提供者
- 動態模塊
- 注入作用域
- 循環依賴
- 模塊參考
- 懶加載模塊
- 應用上下文
- 生命周期事件
- 跨平臺
- 測試
- 技術
- 數據庫
- Mongo
- 配置
- 驗證
- 緩存
- 序列化
- 版本控制
- 定時任務
- 隊列
- 日志
- Cookies
- 事件
- 壓縮
- 文件上傳
- 流式處理文件
- HTTP模塊
- Session(會話)
- MVC
- 性能(Fastify)
- 服務器端事件發送
- 安全
- 認證(Authentication)
- 授權(Authorization)
- 加密和散列
- Helmet
- CORS(跨域請求)
- CSRF保護
- 限速
- GraphQL
- 快速開始
- 解析器(resolvers)
- 變更(Mutations)
- 訂閱(Subscriptions)
- 標量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 聯合類型
- 枚舉(Enums)
- 字段中間件
- 映射類型
- 插件
- 復雜性
- 擴展
- CLI插件
- 生成SDL
- 其他功能
- 聯合服務
- 遷移指南
- Websocket
- 網關
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 適配器
- 微服務
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定義傳輸器
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 獨立應用
- Cli
- 概述
- 工作空間
- 庫
- 用法
- 腳本
- Openapi
- 介紹
- 類型和參數
- 操作
- 安全
- 映射類型
- 裝飾器
- CLI插件
- 其他特性
- 遷移指南
- 秘籍
- CRUD 生成器
- 熱重載
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模塊
- Swagger
- 健康檢查
- CQRS
- 文檔
- Prisma
- 靜態服務
- Nest Commander
- 問答
- Serverless
- HTTP 適配器
- 全局路由前綴
- 混合應用
- HTTPS 和多服務器
- 請求生命周期
- 常見錯誤
- 實例
- 遷移指南
- 發現
- 誰在使用Nest?