## 訂閱(Subscriptions)
除了使用查詢獲取數據和使用突變修改數據之外,`GraphQL` 規范還支持第三種操作類型,稱為訂閱。 `GraphQL` 訂閱是一種將數據從服務器推送到選擇監聽來自服務器的實時消息的客戶端的方式。 訂閱類似于查詢,因為它們指定一組要傳遞給客戶端的字段,但不是立即返回單個答案,而是打開一個通道并將結果發送給客戶端,每次服務器上發生特定事件時 .
訂閱的一個常見用例是向客戶端通知特定事件,例如創建新對象、更新字段等(在[此處](https://www.apollographql.com/docs/react/data/subscriptions)閱讀更多內容)。
```typescript
Subscription: {
commentAdded: {
subscribe: () => pubSub.asyncIterator('commentAdded');
}
}
```
> `pubsub` 是一個 `PubSub` 類的實例。在[這里](https://www.apollographql.com/docs/graphql-subscriptions/setup.html)閱讀更多。
## 使用 Apollo 驅動程序啟用訂閱[#](enable-subscriptions-with-apollo-driver)
要啟用訂閱,請將 `installSubscriptionHandlers` 屬性設置為 `true`。
~~~typescript
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
installSubscriptionHandlers: true,
}),
~~~
> <a style="color:red;">**提醒**:</a>`installSubscriptionHandlers` 配置選項已從最新版本的 Apollo 服務器中刪除,并且很快也會在此包中棄用。 默認情況下,`installSubscriptionHandlers` 將回退到使用 `subscriptions-transport-ws`([閱讀更多](https://github.com/apollographql/subscriptions-transport-ws)),但我們強烈建議使用 `graphql-ws`([閱讀更多](https://github.com/enisdenjo/graphql-ws))庫。
要改用 `graphql-ws` 包,請使用以下配置:
~~~typescript
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
subscriptions: {
'graphql-ws': true
},
}),
~~~
> 您還可以同時使用這兩個包(`subscriptions-transport-ws` 和 `graphql-ws`),例如,為了向后兼容。
### 代碼優先[#](#code-first)
要使用代碼優先方法創建訂閱,我們使用 `@Subscription() `裝飾器(從 `@nestjs/graphql `包中導出)和來自 `graphql-subscriptions `包的 `PubSub`類,它提供了一個簡單的發布/訂閱 API。
以下訂閱處理程序通過調用 `PubSub#asyncIterator` 來處理訂閱事件。 此方法采用單個參數 `triggerName`,它對應于事件主題名稱。
~~~typescript
const pubSub = new PubSub();
@Resolver((of) => Author)
export class AuthorResolver {
// ...
@Subscription((returns) => Comment)
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
}
~~~
>所有裝飾器都是從 `@nestjs/graphql` 包中導出的,而 `PubSub` 類是從 `graphql-subscriptions` 包中導出的。
> <a style="color:orange;">**筆記:**</a> `PubSub` 是一個公開簡單的發布和訂閱 API 的類。 在此處閱讀更多相關信息。 請注意,`Apollo` 文檔警告說默認實現不適合生產(在[此處](https://www.apollographql.com/docs/apollo-server/data/subscriptions/)閱讀更多信息)。 生產應用程序應使用由外部商店支持的 `PubSub` 實現(在[此處](https://github.com/apollographql/graphql-subscriptions#pubsub-implementations)閱讀更多信息)。
這將導致在 SDL 中生成 GraphQL 架構的以下部分:
~~~graphql
type Subscription {
commentAdded(): Comment!
}
~~~
請注意,根據定義,訂閱返回一個具有單個頂級屬性的對象,其鍵是訂閱的名稱。 此名稱要么繼承自訂閱處理程序方法的名稱(即上面的 `commentAdded`),要么通過將帶有鍵名稱的選項作為第二個參數傳遞給` @Subscription() `裝飾器來顯式提供,如下所示。
~~~typescript
@Subscription(returns => Comment, {
name: 'commentAdded',
})
subscribeToCommentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
此構造生成與前面的代碼示例相同的 SDL,但允許我們將方法名稱與訂閱分離。
### 發布[#](#publishing)
現在,要發布事件,我們使用 `PubSub#publish` 方法。 這通常在突變中使用,以在對象圖的一部分發生更改時觸發客戶端更新。 例如:
>posts/posts.resolver.ts
~~~typescript
@Mutation(returns => Post)
async addComment(
@Args('postId', { type: () => Int }) postId: number,
@Args('comment', { type: () => Comment }) comment: CommentInput,
) {
const newComment = this.commentsService.addComment({ id: postId, comment });
pubSub.publish('commentAdded', { commentAdded: newComment });
return newComment;
}
~~~
`PubSub#publish` 方法將 `triggerName`(同樣,將其視為事件主題名稱)作為第一個參數,將事件有效負載作為第二個參數。 如前所述,根據定義,訂閱返回一個值并且該值具有形狀。 再次查看為我們的 `commentAdded` 訂閱生成的 SDL:
~~~graphql
type Subscription {
commentAdded(): Comment!
}
~~~
這告訴我們訂閱必須返回一個頂級屬性名稱為 `commentAdded` 的對象,該對象的值是一個 `Comment `對象。 需要注意的重要一點是,`PubSub#publish` 方法發出的事件負載的形狀必須與預期從訂閱返回的值的形狀相對應。 因此,在我們上面的示例中,`pubSub.publish('commentAdded', { commentAdded: newComment })` 語句發布了一個帶有適當形狀有效負載的 `commentAdded `事件。 如果這些形狀不匹配,您的訂閱將在 `GraphQL` 驗證階段失敗。
### 過濾訂閱[#](#filtering-subscriptions)
要過濾掉特定事件,請將過濾器屬性設置為過濾器函數。 此函數的作用類似于傳遞給數組過濾器的函數。 它有兩個參數:包含事件有效負載(由事件發布者發送)的有效負載,以及接受訂閱請求期間傳入的任何參數的變量。 它返回一個布爾值,確定是否應將此事件發布給客戶端偵聽器。
~~~typescript
@Subscription(returns => Comment, {
filter: (payload, variables) =>
payload.commentAdded.title === variables.title,
})
commentAdded(@Args('title') title: string) {
return pubSub.asyncIterator('commentAdded');
}
~~~
### 改變訂閱負載[#](#mutating-subscription-payloads)
要改變已發布的事件負載,請將 `resolve` 屬性設置為函數。 該函數接收事件有效負載(由事件發布者發送)并返回適當的值。
~~~typescript
@Subscription(returns => Comment, {
resolve: value => value,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
> <a style="color:orange;">**筆記:**</a>如果你使用 `resolve` 選項,你應該返回解包后的負載(例如,在我們的例子中,直接返回一個 `newComment`對象,而不是一個 `{ commentAdded: newComment }` 對象)。
如果您需要訪問注入的提供者(例如,使用外部服務來驗證數據),請使用以下構造。
~~~typescript
@Subscription(returns => Comment, {
resolve(this: AuthorResolver, value) {
// "this" refers to an instance of "AuthorResolver"
return value;
}
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
相同的結構適用于過濾器:
~~~typescript
@Subscription(returns => Comment, {
filter(this: AuthorResolver, payload, variables) {
// "this" refers to an instance of "AuthorResolver"
return payload.commentAdded.title === variables.title;
}
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
### 架構優先[#](#schema-first)
要在 Nest 中創建等效訂閱,我們將使用 `@Subscription()`裝飾器。
~~~typescript
const pubSub = new PubSub();
@Resolver('Author')
export class AuthorResolver {
// ...
@Subscription()
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
}
~~~
要根據上下文和參數過濾掉特定事件,請設置過濾器(`filter`)屬性。
~~~typescript
@Subscription('commentAdded', {
filter: (payload, variables) =>
payload.commentAdded.title === variables.title,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
要改變已發布的有效負載,我們可以使用解析(`resolve`)函數。
~~~typescript
@Subscription('commentAdded', {
resolve: value => value,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
如果您需要訪問注入的提供程序(例如,使用外部服務來驗證數據),請使用以下構造:
~~~typescript
@Subscription('commentAdded', {
resolve(this: AuthorResolver, value) {
// "this" refers to an instance of "AuthorResolver"
return value;
}
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
相同的結構適用于過濾器:
~~~typescript
@Subscription('commentAdded', {
filter(this: AuthorResolver, payload, variables) {
// "this" refers to an instance of "AuthorResolver"
return payload.commentAdded.title === variables.title;
}
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
~~~
最后一步是更新類型定義文件。
~~~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
}
type Comment {
id: String
content: String
}
type Subscription {
commentAdded(title: String!): Comment
}
~~~
有了這個,我們創建了一個 commentAdded(title: String!): Comment 訂閱。 您可以在[此處](https://github.com/nestjs/nest/blob/master/sample/12-graphql-schema-first)找到完整的示例實現。
### 發布訂閱[#](#pubsub)
我們在上面實例化了一個本地 `PubSub` 實例。 首選方法是將 `PubSub` 定義為提供者并通過構造函數注入它(使用 `@Inject()` 裝飾器)。 這允許我們在整個應用程序中重用該實例。 例如,如下定義一個提供者,然后在需要的地方注入`“PUB_SUB”`。
~~~typescript
{
provide: 'PUB_SUB',
useValue: new PubSub(),
}
~~~
### 自定義訂閱服務器[#](#customize-subscriptions-server)
要自定義訂閱服務器(例如,更改路徑),請使用訂閱(`subscriptions`)選項屬性。
~~~typescript
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
subscriptions: {
'subscriptions-transport-ws': {
path: '/graphql'
},
}
}),
~~~
如果您使用 `graphql-ws` 包進行訂閱,請將 `subscriptions-transport-ws` 鍵替換為 `graphql-ws`,如下所示:
~~~typescript
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
subscriptions: {
'graphql-ws': {
path: '/graphql'
},
}
}),
~~~
### 通過 WebSocket 進行身份驗證[#](#authentication-over-websockets)
檢查用戶是否通過身份驗證可以在 `onConnect` 回調函數中完成,您可以在訂閱選項中指定該回調函數。
`onConnect` 將接收傳遞給 `SubscriptionClient` 的 `connectionParams` 作為第一個參數([閱讀更多](https://www.apollographql.com/docs/react/data/subscriptions/#5-authenticate-over-websocket-optional))。
~~~typescript
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
subscriptions: {
'subscriptions-transport-ws': {
onConnect: (connectionParams) => {
const authToken = connectionParams.authToken;
if (!isValid(authToken)) {
throw new Error('Token is not valid');
}
// extract user information from token
const user = parseToken(authToken);
// return user info to add them to the context later
return { user };
},
}
},
context: ({ connection }) => {
// connection.context will be equal to what was returned by the "onConnect" callback
},
}),
~~~
此示例中的 `authToken` 僅在首次建立連接時由客戶端發送一次。 使用此連接進行的所有訂閱都將具有相同的 `authToken`,因此具有相同的用戶信息。
> <a style="color:orange;">**筆記:**</a>`subscriptions-transport-ws` 中有一個錯誤,允許連接跳過 `onConnect` 階段([閱讀更多](https://github.com/apollographql/subscriptions-transport-ws/issues/349))。 您不應假設在用戶開始訂閱時調用了 `onConnect`,并且始終檢查上下文是否已填充。
如果您使用的是 `graphql-ws` 包,`onConnect` 回調的簽名會略有不同:
~~~typescript
subscriptions: {
'graphql-ws': {
onConnect: (context: Context<any>) => {
const { connectionParams, extra } = context;
// user validation will remain the same as in the example above
// when using with graphql-ws, additional context value should be stored in the extra field
extra.user = { user: {} };
},
},
context: ({ extra }) => {
// you can now access your additional context value through the extra field
}
},
~~~
## 使用 Mercurius 驅動程序啟用訂閱[#](#enable-subscriptions-with-mercurius-driver)
要啟用訂閱,請將訂閱(`subscription`)屬性設置為 `true`。
~~~typescript
GraphQLModule.forRoot<MercuriusDriverConfig>({
driver: MercuriusDriver,
subscription: true,
}),
~~~
> **提示**:您還可以傳遞選項對象來設置自定義發射器、驗證傳入連接等。在[此處](https://github.com/mercurius-js/mercurius/blob/master/docs/api/options.md#plugin-options)閱讀更多信息(請參閱訂閱)。
### 代碼優先[#](#code-first-1)
要使用代碼優先的方法創建訂閱,我們使用 `@Subscription()` 裝飾器(從 `@nestjs/graphql `包中導出)和 `mercurius` 包中的 `PubSub` 類,它提供了一個簡單的發布/訂閱 API。
以下訂閱處理程序通過調用 `PubSub#asyncIterator` 來處理訂閱事件。 此方法采用單個參數 `triggerName`,它對應于事件主題名稱。
~~~typescript
@Resolver((of) => Author)
export class AuthorResolver {
// ...
@Subscription((returns) => Comment)
commentAdded(@Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
}
~~~
上面示例中使用的所有裝飾器都是從 `@nestjs/graphql`包中導出的,而 `PubSub` 類是從 `mercurius` 包中導出的。
`PubSub` 是一個公開簡單的發布和訂閱 API 的類。 查看此部分,了解如何注冊自定義 `PubSub`類。
這將導致在 SDL 中生成 GraphQL 架構的以下部分:
~~~graphql
type Subscription {
commentAdded(): Comment!
}
~~~
請注意,根據定義,訂閱返回一個具有單個頂級屬性的對象,其鍵是訂閱的名稱。 此名稱要么繼承自訂閱處理程序方法的名稱(即上面的 `commentAdded`),要么通過將帶有鍵名稱的選項作為第二個參數傳遞給 `@Subscription()` 裝飾器來顯式提供,如下所示。
~~~typescript
@Subscription(returns => Comment, {
name: 'commentAdded',
})
subscribeToCommentAdded(@Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
~~~
此構造生成與前面的代碼示例相同的 SDL,但允許我們將方法名稱與訂閱分離。
### 發布[#](#publishing-1)
現在,要發布事件,我們使用 `PubSub#publish` 方法。 這通常在突變中使用,以在對象圖的一部分發生更改時觸發客戶端更新。 例如:
>posts/posts.resolver.ts
~~~typescript
@Mutation(returns => Post)
async addComment(
@Args('postId', { type: () => Int }) postId: number,
@Args('comment', { type: () => Comment }) comment: CommentInput,
@Context('pubsub') pubSub: PubSub,
) {
const newComment = this.commentsService.addComment({ id: postId, comment });
await pubSub.publish({
topic: 'commentAdded',
payload: {
commentAdded: newComment
}
});
return newComment;
}
~~~
如前所述,根據定義,訂閱返回一個值并且該值具有形狀。 再次查看為我們的 `commentAdded`訂閱生成的 SDL:
~~~graphql
type Subscription {
commentAdded(): Comment!
}
~~~
這告訴我們訂閱必須返回一個頂級屬性名稱為 `commentAdded`的對象,該對象的值是一個 `Comment` 對象。 需要注意的重要一點是,`PubSub#publish` 方法發出的事件負載的形狀必須與預期從訂閱返回的值的形狀相對應。 因此,在我們上面的示例中, `pubSub.publish({ topic: 'commentAdded', payload: { commentAdded: newComment } })`語句發布了一個帶有適當形狀的有效負載的 `commentAdded` 事件。 如果這些類型不匹配,您的訂閱將在 GraphQL 驗證階段失敗。
### 過濾訂閱[#](#filtering-subscriptions-1)
要過濾掉特定事件,請將過濾器屬性設置為過濾器函數。 此函數的作用類似于傳遞給數組過濾器的函數。 它有兩個參數:包含事件有效負載(由事件發布者發送)的有效負載,以及接受訂閱請求期間傳入的任何參數的變量。 它返回一個布爾值,確定是否應將此事件發布給客戶端偵聽器。
~~~typescript
@Subscription(returns => Comment, {
filter: (payload, variables) =>
payload.commentAdded.title === variables.title,
})
commentAdded(@Args('title') title: string, @Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
~~~
如果您需要訪問注入的提供者(例如,使用外部服務來驗證數據),請使用以下構造。
~~~typescript
@Subscription(returns => Comment, {
filter(this: AuthorResolver, payload, variables) {
// "this" refers to an instance of "AuthorResolver"
return payload.commentAdded.title === variables.title;
}
})
commentAdded(@Args('title') title: string, @Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
~~~
### 架構優先#](#schema-first-1)
要在 Nest 中創建等效訂閱,我們將使用 `@Subscription()` 裝飾器。
~~~typescript
const pubSub = new PubSub();
@Resolver('Author')
export class AuthorResolver {
// ...
@Subscription()
commentAdded(@Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
}
~~~
要根據上下文和參數過濾掉特定事件,請設置過濾器屬性。
~~~typescript
@Subscription('commentAdded', {
filter: (payload, variables) =>
payload.commentAdded.title === variables.title,
})
commentAdded(@Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
~~~
如果您需要訪問注入的提供程序(例如,使用外部服務來驗證數據),請使用以下構造:
~~~typescript
@Subscription('commentAdded', {
filter(this: AuthorResolver, payload, variables) {
// "this" refers to an instance of "AuthorResolver"
return payload.commentAdded.title === variables.title;
}
})
commentAdded(@Context('pubsub') pubSub: PubSub) {
return pubSub.subscribe('commentAdded');
}
~~~
最后一步是更新類型定義文件。
~~~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
}
type Comment {
id: String
content: String
}
type Subscription {
commentAdded(title: String!): Comment
}
~~~
有了這個,我們創建了一個 `commentAdded(title: String!): Comment` 訂閱。
#### 發布訂閱[#](#pubsub-1)
在上面的示例中,我們使用了默認的 `PubSub` 發射器 (`mqemitter`) 首選方法(用于生產)是使用 `mqemitter-redis`。 或者,可以提供自定義 `PubSub` 實現(在[此處](https://github.com/mercurius-js/mercurius/blob/master/docs/subscriptions.md)閱讀更多信息)
~~~typescript
GraphQLModule.forRoot<MercuriusDriverConfig>({
driver: MercuriusDriver,
subscription: {
emitter: require('mqemitter-redis')({
port: 6579,
host: '127.0.0.1',
}),
},
});
~~~
### 通過 WebSocket 進行身份驗證[#](#authentication-over-websockets-1)
檢查用戶是否通過身份驗證可以在您可以在訂閱選項中指定的 `verifyClient`回調函數中完成。
`verifyClient` 將接收` info` 對象作為第一個參數,您可以使用它來檢索請求的標頭。
~~~typescript
GraphQLModule.forRoot<MercuriusDriverConfig>({
driver: MercuriusDriver,
subscription: {
verifyClient: (info, next) => {
const authorization = info.req.headers?.authorization as string;
if (!authorization?.startsWith('Bearer ')) {
return next(false);
}
next(true);
},
}
}),
~~~
<!-- tabs:start -->
#### ** 模式優先 **
為了以 Nest 方式創建等效訂閱,我們將使用 `@Subscription()` 裝飾器。
```typescript
const pubSub = new PubSub();
@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 });
}
@Subscription()
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
}
```
為了根據上下文和參數過濾掉特定事件,我們可以設置一個 filter 屬性。
```typescript
@Subscription('commentAdded', {
filter: (payload, variables) =>
payload.commentAdded.repositoryName === variables.repoFullName,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
```
為了改變已發布的有效負載,我們可以使用 resolve 函數。
```typescript
@Subscription('commentAdded', {
resolve: value => value,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
```
### 類型定義
最后一步是更新類型定義文件。
```typescript
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int!
title: String
votes: Int
}
type Query {
author(id: Int!): Author
}
type Comment {
id: String
content: String
}
type Subscription {
commentAdded(repoFullName: String!): Comment
}
```
做得好。我們創建了一個 commentAdded(repoFullName: String!): Comment 訂閱。您可以在[此處](https://github.com/nestjs/nest/blob/master/sample/12-graphql-apollo)找到完整的示例實現。
#### ** 使用 Typescript **
要使用 class-first 方法創建訂閱,我們將使用 @Subscription() 裝飾器。
```typescript
const pubSub = new PubSub();
@Resolver('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 });
}
@Subscription(returns => Comment)
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
}
```
為了根據上下文和參數過濾掉特定事件,我們可以設置 filter 屬性。
```typescript
@Subscription(returns => Comment, {
filter: (payload, variables) =>
payload.commentAdded.repositoryName === variables.repoFullName,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
```
為了改變已發布的有效負載,我們可以使用 resolve 函數。
```typescript
@Subscription(returns => Comment, {
resolve: value => value,
})
commentAdded() {
return pubSub.asyncIterator('commentAdded');
}
```
<!-- tabs:end -->
### Pubsub
我們在這里使用了一個本地 `PubSub` 實例。相反, 我們應該將 `PubSub` 定義為一個組件, 通過構造函數 (使用 `@Inject ()` 裝飾器) 注入它, 并在整個應用程序中重用它。[您可以在此了解有關嵌套自定義組件的更多信息](/8/fundamentals?id=自定義providercustomer-provider)。
```typescript
{
provide: 'PUB_SUB',
useValue: new PubSub(),
}
```
### Module
為了啟用訂閱,我們必須將 `installSubscriptionHandlers` 屬性設置為 `true` 。
```typescript
GraphQLModule.forRoot({
typePaths: ['./**/*.graphql'],
installSubscriptionHandlers: true,
}),
```
要自定義訂閱服務器(例如,更改端口),您可以使用 `subscriptions` 屬性(閱讀[更多](https://www.apollographql.com/docs/apollo-server/v2/api/apollo-server.html#constructor-options-lt-ApolloServer-gt))。
- 介紹
- 概述
- 第一步
- 控制器
- 提供者
- 模塊
- 中間件
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 自定義裝飾器
- 基礎知識
- 自定義提供者
- 異步提供者
- 動態模塊
- 注入作用域
- 循環依賴
- 模塊參考
- 懶加載模塊
- 應用上下文
- 生命周期事件
- 跨平臺
- 測試
- 技術
- 數據庫
- 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?