<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 聯合服務 [<span style="color:red">Apollo 聯合服務</span>](https://www.apollographql.com/docs/federation/)提供了一種將單體式 GraphQL 服務器拆分為獨立微服務的手段。它有兩個組成部分:一個網關和一或多個聯合微服務。每個微服務都持有部分 schema,網關將這些 schema 合并為一個可以被客戶端使用的 schema。 引用[<span style="color:red">Apollo 文檔</span>](https://www.apollographql.com/blog/announcement/apollo-federation-f260cf525d21/),聯合服務的設計遵循以下核心原則: - 構建圖表應該是**聲明式**的。使用聯合服務,你可以在 schema 內部聲明式地組合圖表,而不是編寫命令式 schema 拼接代碼。 - 代碼應該按**關注點**分割,而不是按類型。通常沒有一個團隊能控制像 User 或 Product 這種重要類型的各個方面,因此這些類型的定義應該分布在團隊和代碼庫中,而不是寫在一起。 - 圖表應盡可能簡單,以讓客戶端使用。同時,聯合服務可以形成一個完整的、以產品為中心的圖表,準確地反映它在客戶端的使用情況。 - 它只是 GraphQL,僅使用符合規范的語言特性。任何語言,不僅僅是 JavaScript,都可以實現聯合服務。 > Apollo 聯合服務到目前為止還不支持訂閱。 在接下來的例子中,我們將設置一個帶有網關和兩個聯合端點的演示程序:一個 Users 服務和一個 Posts 服務, ### 聯合示例:Users 首先,安裝聯合服務的依賴包: ```bash npm install --save @apollo/federation @apollo/subgraph ``` ### 模式優先 Users 服務有一個簡單的 schema。注意 `@key` 這個指令:它告訴 Apollo 查詢規劃器,如果你有它的 `id`,則可以獲取特定的 User 實例。另外,請注意我們也要繼承這個 `Query` 類型。 ```graphql type User @key(fields: "id") { id: ID! name: String! } extend type Query { getUser(id: ID!): User } ``` 我們的解析器有一個額外的方法:`resolveReference()`。每當相關資源需要 User 實例時,它就會被 Apollo 網關調用。我們在后面的 Posts 服務中也會看到這個例子。請注意 `@ResolveReference()` 這個裝飾器。 ```typescript import { Args, Query, Resolver, ResolveReference } from '@nestjs/graphql'; import { UsersService } from './users.service'; @Resolver('User') export class UsersResolvers { constructor(private usersService: UsersService) {} @Query() getUser(@Args('id') id: string) { return this.usersService.findById(id); } @ResolveReference() resolveReference(reference: { __typename: string; id: string }) { return this.usersService.findById(reference.id); } } ``` 最后,我們在模塊中使用 `GraphQLFederationModule` 將所有東西連接起來。此模塊接收與常規的 `GraphQLModule` 相同的配置。 ```typescript import { Module } from '@nestjs/common'; import { GraphQLFederationModule } from '@nestjs/graphql'; import { UsersResolvers } from './users.resolvers'; @Module({ imports: [ GraphQLFederationModule.forRoot({ typePaths: ['**/*.graphql'], }), ], providers: [UsersResolvers], }) export class AppModule {} ``` ### 代碼優先 代碼優先聯合服務與常規的代碼優先 GraphQL 很像。我們只需添加一些額外的裝飾器到 `User` 實體即可。 ```typescript import { Directive, Field, ID, ObjectType } from '@nestjs/graphql'; @ObjectType() @Directive('@key(fields: "id")') export class User { @Field((type) => ID) id: number; @Field() name: string; } ``` 我們的解析器有一個額外的方法:`resolveReference()`。每當相關資源需要 User 實例時,它就會被 Apollo 網關調用。我們在后面的 Posts 服務中也會看到這個例子。請注意 `@ResolveReference()` 這個裝飾器。 ```typescript import { Args, Query, Resolver, ResolveReference } from '@nestjs/graphql'; import { User } from './user.entity'; import { UsersService } from './users.service'; @Resolver((of) => User) export class UsersResolvers { constructor(private usersService: UsersService) {} @Query((returns) => User) getUser(@Args('id') id: number): User { return this.usersService.findById(id); } @ResolveReference() resolveReference(reference: { __typename: string; id: number }): User { return this.usersService.findById(reference.id); } } ``` 最后,我們在模塊中使用 `GraphQLFederationModule` 將所有東西連接起來。此模塊接收與常規的 `GraphQLModule` 相同的配置。 ```typescript import { Module } from '@nestjs/common'; import { GraphQLFederationModule } from '@nestjs/graphql'; import { UsersResolvers } from './users.resolvers'; import { UsersService } from './users.service'; // Not included in this example @Module({ imports: [ GraphQLFederationModule.forRoot({ autoSchemaFile: true, }), ], providers: [UsersResolvers, UsersService], }) export class AppModule {} ``` ### 聯合示例:Posts 我們的 Post 服務通過 `getPosts` 查詢提供文章聚合,同時也使用 `user.posts` 來擴展我們的 `User` 類型。 ### 模式優先 Posts 服務在它的 schema 中通過用 `extend` 關鍵字標記來引用 User 類型。它還向 User 類型添加了一個屬性。請注意用于匹配 User 實例的 `@key` 指令,以及指示 `id` 字段在別處管理的 `@external` 指令。 ```graphql type Post @key(fields: "id") { id: ID! title: String! body: String! user: User } extend type User @key(fields: "id") { id: ID! @external posts: [Post] } extend type Query { getPosts: [Post] } ``` 在我們的解析器這里有一個有趣的方法:`getUser()`。它返回一個引用,其中包含 `__typename` 和應用程序解析引用所需的任何其他屬性,在這個例子中僅是一個屬性 `id`。`__typename`被 GraphQL 網關用來精確定位負責 User 類型和請求實例的微服務。上面討論的 Users 服務將在 `resolveReference()` 方法上被調用。 ```typescript import { Query, Resolver, Parent, ResolveField } from '@nestjs/graphql'; import { PostsService } from './posts.service'; import { Post } from './posts.interfaces'; @Resolver('Post') export class PostsResolvers { constructor(private postsService: PostsService) {} @Query('getPosts') getPosts() { return this.postsService.findAll(); } @ResolveField('user') getUser(@Parent() post: Post) { return { __typename: 'User', id: post.userId }; } } ``` Posts 服務幾乎具有和 Users 相同的模塊,但為了完整起見,我們在下面將它包含進來: ```typescript import { Module } from '@nestjs/common'; import { GraphQLFederationModule } from '@nestjs/graphql'; import { PostsResolvers } from './posts.resolvers'; @Module({ imports: [ GraphQLFederationModule.forRoot({ typePaths: ['**/*.graphql'], }), ], providers: [PostsResolvers], }) export class AppModule {} ``` ### 代碼優先 我們需要創建一個代表我們的 User 實體的類。即使它存在于其他服務中,我們也將使用和繼承它。注意 `@extends` 和 `@external` 指令。 ```typescript import { Directive, ObjectType, Field, ID } from '@nestjs/graphql'; import { Post } from './post.entity'; @ObjectType() @Directive('@extends') @Directive('@key(fields: "id")') export class User { @Field((type) => ID) @Directive('@external') id: number; @Field((type) => [Post]) posts?: Post[]; } ``` 我們在 `User` 實體上為我們的擴展創建解析器,如下所示: ```typescript import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; import { PostsService } from './posts.service'; import { Post } from './post.entity'; import { User } from './user.entity'; @Resolver((of) => User) export class UsersResolvers { constructor(private readonly postsService: PostsService) {} @ResolveField((of) => [Post]) public posts(@Parent() user: User): Post[] { return this.postsService.forAuthor(user.id); } } ``` 我們還需要創建我們的 `Post` 實體: ```typescript import { Directive, Field, ID, Int, ObjectType } from '@nestjs/graphql'; import { User } from './user.entity'; @ObjectType() @Directive('@key(fields: "id")') export class Post { @Field((type) => ID) id: number; @Field() title: string; @Field((type) => Int) authorId: number; @Field((type) => User) user?: User; } ``` 還有它的解析器: ```typescript import { Query, Args, ResolveField, Resolver, Parent } from '@nestjs/graphql'; import { PostsService } from './posts.service'; import { Post } from './post.entity'; import { User } from './user.entity'; @Resolver((of) => Post) export class PostsResolvers { constructor(private readonly postsService: PostsService) {} @Query((returns) => Post) findPost(@Args('id') id: number): Post { return this.postsService.findOne(id); } @Query((returns) => [Post]) getPosts(): Post[] { return this.postsService.all(); } @ResolveField((of) => User) user(@Parent() post: Post): any { return { __typename: 'User', id: post.authorId }; } } ``` 最后,在模塊中把它們串聯起來。注意 schema 構建配置,在這里我們指定 `User` 為外部類型。 ```typescript import { Module } from '@nestjs/common'; import { GraphQLFederationModule } from '@nestjs/graphql'; import { User } from './user.entity'; import { PostsResolvers } from './posts.resolvers'; import { UsersResolvers } from './users.resolvers'; import { PostsService } from './posts.service'; // Not included in example @Module({ imports: [ GraphQLFederationModule.forRoot({ autoSchemaFile: true, buildSchemaOptions: { orphanedTypes: [User], }, }), ], providers: [PostsResolvers, UsersResolvers, PostsService], }) export class AppModule {} ``` ### 聯合示例:網關 首先,安裝網關的依賴包: ```bash $ npm install --save @apollo/gateway ``` 我們的網關只需要一個端點列表,它會從那里自動發現所有的 schemas。因為代碼和模式優先是一樣的,所以網關的代碼很短: ```typescript import { Module } from '@nestjs/common'; import { GraphQLGatewayModule } from '@nestjs/graphql'; @Module({ imports: [ GraphQLGatewayModule.forRoot({ server: { // ... Apollo server options cors: true, }, gateway: { serviceList: [ { name: 'users', url: 'http://user-service/graphql' }, { name: 'posts', url: 'http://post-service/graphql' }, ], }, }), ], }) export class AppModule {} ``` 代碼優先模式和架構優先模式在此處提供了[一個](https://github.com/nestjs/nest/tree/master/sample/32-graphql-federation-schema-first/gateway)[工作](https://github.com/nestjs/nest/tree/master/sample/31-graphql-federation-code-first/gateway)示例。[](https://github.com/nestjs/nest/tree/master/sample/32-graphql-federation-schema-first/gateway) > Apollo 建議你不要依賴生產環境中的服務發現,而是使用它們的[圖表管理器](https://www.apollographql.com/docs/federation/managed-federation/overview/) ### 與 ` Mercurius`聯合 首先安裝所需的依賴項: ~~~bash $ npm install --save @apollo/subgraph @nestjs/mercurius ~~~ > **筆記**:`@apollo/subgraph` 包是構建子圖模式(`buildSubgraphSchema`、`printSubgraphSchema` 函數)所必需的。 #### 架構優先 “用戶服務”提供了一個簡單的模式。請注意`@key` 指令:它指示 Mercurius 查詢計劃器,如果您指定其 id,則可以獲取特定的 User 實例。另外,請注意我們擴展了 `Query` 類型。 ~~~graphql type User @key(fields: "id") { id: ID! name: String! } extend type Query { getUser(id: ID!): User } ~~~ `Resolver` 提供了另一種名為 `resolveReference()` 的方法。 每當相關資源需要用戶實例時,此方法由 Mercurius 網關觸發。 稍后我們將在 `Posts` 服務中看到一個這樣的例子。 請注意,該方法必須使用 `@ResolveReference()` 裝飾器進行注釋。 ~~~typescript import { Args, Query, Resolver, ResolveReference } from '@nestjs/graphql'; import { UsersService } from './users.service'; @Resolver('User') export class UsersResolver { constructor(private usersService: UsersService) {} @Query() getUser(@Args('id') id: string) { return this.usersService.findById(id); } @ResolveReference() resolveReference(reference: { __typename: string; id: string }) { return this.usersService.findById(reference.id); } } ~~~ 最后,我們通過在配置對象中注冊傳遞 `MercuriusFederationDriver` 驅動程序的 `GraphQLModule` 來連接所有內容: ~~~typescript import { MercuriusFederationDriver, MercuriusFederationDriverConfig, } from '@nestjs/mercurius'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { UsersResolver } from './users.resolver'; @Module({ imports: [ GraphQLModule.forRoot<MercuriusFederationDriverConfig>({ driver: MercuriusFederationDriver, typePaths: ['**/*.graphql'], federationMetadata: true, }), ], providers: [UsersResolver], }) export class AppModule {} ~~~ ### 聯合示例:Posts 我們的 Post 服務通過 `getPosts` 查詢提供文章聚合,同時也使用 `user.posts` 來擴展我們的 `User` 類型。 #### 架構優先 `“Posts service”`通過用 extend 關鍵字標記它來引用其架構中的用戶類型。 它還在用戶類型(帖子)上聲明了一個附加屬性。 請注意用于匹配 `User `實例的 `@key` 指令,以及指示 id 字段在其他地方管理的 `@external `指令。 ~~~graphql type Post @key(fields: "id") { id: ID! title: String! body: String! user: User } extend type User @key(fields: "id") { id: ID! @external posts: [Post] } extend type Query { getPosts: [Post] } ~~~ 在下面的示例中,PostsResolver 提供了 getUser() 方法,該方法返回包含 __typename 的引用和您的應用程序可能需要解析引用的一些附加屬性,在本例中為 id。 __typename 被 GraphQL 網關用來查明負責用戶類型的微服務并檢索相應的實例。 執行 resolveReference() 方法時將請求上述“用戶服務”。 ~~~typescript import { Query, Resolver, Parent, ResolveField } from '@nestjs/graphql'; import { PostsService } from './posts.service'; import { Post } from './posts.interfaces'; @Resolver('Post') export class PostsResolver { constructor(private postsService: PostsService) {} @Query('getPosts') getPosts() { return this.postsService.findAll(); } @ResolveField('user') getUser(@Parent() post: Post) { return { __typename: 'User', id: post.userId }; } } ~~~ 最后,我們必須注冊 `GraphQLModule`,類似于我們在“用戶服務”部分中所做的。 ~~~typescript import { MercuriusFederationDriver, MercuriusFederationDriverConfig, } from '@nestjs/mercurius'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { PostsResolver } from './posts.resolver'; @Module({ imports: [ GraphQLModule.forRoot<MercuriusFederationDriverConfig>({ driver: MercuriusFederationDriver, federationMetadata: true, typePaths: ['**/*.graphql'], }), ], providers: [PostsResolvers], }) export class AppModule {} ~~~ #### 代碼優先 首先,我們必須聲明一個代表用戶實體的類。 盡管實體本身存在于另一個服務中,但我們將在此處使用它(擴展其定義)。 注意@extends 和@external 指令。 ~~~ts import { Directive, ObjectType, Field, ID } from '@nestjs/graphql'; import { Post } from './post.entity'; @ObjectType() @Directive('@extends') @Directive('@key(fields: "id")') export class User { @Field((type) => ID) @Directive('@external') id: number; @Field((type) => [Post]) posts?: Post[]; } ~~~ 現在讓我們在 User 實體上為我們的擴展創建相應的解析器,如下所示: ~~~ts import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; import { PostsService } from './posts.service'; import { Post } from './post.entity'; import { User } from './user.entity'; @Resolver((of) => User) export class UsersResolver { constructor(private readonly postsService: PostsService) {} @ResolveField((of) => [Post]) public posts(@Parent() user: User): Post[] { return this.postsService.forAuthor(user.id); } } ~~~ 我們還必須定義 Post 實體類: ~~~ts import { Directive, Field, ID, Int, ObjectType } from '@nestjs/graphql'; import { User } from './user.entity'; @ObjectType() @Directive('@key(fields: "id")') export class Post { @Field((type) => ID) id: number; @Field() title: string; @Field((type) => Int) authorId: number; @Field((type) => User) user?: User; } ~~~ 及其解析器: ~~~ts import { Query, Args, ResolveField, Resolver, Parent } from '@nestjs/graphql'; import { PostsService } from './posts.service'; import { Post } from './post.entity'; import { User } from './user.entity'; @Resolver((of) => Post) export class PostsResolver { constructor(private readonly postsService: PostsService) {} @Query((returns) => Post) findPost(@Args('id') id: number): Post { return this.postsService.findOne(id); } @Query((returns) => [Post]) getPosts(): Post[] { return this.postsService.all(); } @ResolveField((of) => User) user(@Parent() post: Post): any { return { __typename: 'User', id: post.authorId }; } } ~~~ 最后,將其捆綁在一個模塊中。 請注意架構構建選項,我們在其中指定 User 是孤立(外部)類型。 ~~~ts import { MercuriusFederationDriver, MercuriusFederationDriverConfig, } from '@nestjs/mercurius'; import { Module } from '@nestjs/common'; import { User } from './user.entity'; import { PostsResolvers } from './posts.resolvers'; import { UsersResolvers } from './users.resolvers'; import { PostsService } from './posts.service'; // Not included in example @Module({ imports: [ GraphQLModule.forRoot<MercuriusFederationDriverConfig>({ driver: MercuriusFederationDriver, autoSchemaFile: true, federationMetadata: true, buildSchemaOptions: { orphanedTypes: [User], }, }), ], providers: [PostsResolver, UsersResolver, PostsService], }) export class AppModule {} ~~~ ### 聯合示例:網關 網關需要指定一個端點列表,它將自動發現相應的模式。因此,對于代碼優先和架構優先方法,網關服務的實現將保持不變。 ~~~typescript import { MercuriusGatewayDriver, MercuriusGatewayDriverConfig, } from '@nestjs/mercurius'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; @Module({ imports: [ GraphQLModule.forRoot<MercuriusGatewayDriverConfig>({ driver: MercuriusGatewayDriver, gateway: { services: [ { name: 'users', url: 'http://user-service/graphql' }, { name: 'posts', url: 'http://post-service/graphql' }, ], }, }), ], }) export class AppModule {} ~~~ ### 共享上下文 你可以通過一個構建服務來自定義網關和聯合服務之間的請求。這讓你能夠共享有關請求的上下文。你能輕松繼承默認的 `RemoteGraphQLDataSource` 并實現其中一個鉤子。有關可能性的更多信息,請參閱 [Apollo 文檔](https://www.apollographql.com/docs/federation/api/apollo-gateway/#class-remotegraphqldatasource)中的 `RemoteGraphQLDataSource` 章節. ```typescript import { Module } from '@nestjs/common'; import { GATEWAY_BUILD_SERVICE, GraphQLGatewayModule } from '@nestjs/graphql'; import { RemoteGraphQLDataSource } from '@apollo/gateway'; import { decode } from 'jsonwebtoken'; class AuthenticatedDataSource extends RemoteGraphQLDataSource { async willSendRequest({ request, context }) { const { userId } = await decode(context.jwt); request.http.headers.set('x-user-id', userId); } } @Module({ providers: [ { provide: AuthenticatedDataSource, useValue: AuthenticatedDataSource, }, { provide: GATEWAY_BUILD_SERVICE, useFactory: (AuthenticatedDataSource) => { return ({ name, url }) => new AuthenticatedDataSource({ url }); }, inject: [AuthenticatedDataSource], }, ], exports: [GATEWAY_BUILD_SERVICE], }) class BuildServiceModule {} @Module({ imports: [ GraphQLGatewayModule.forRootAsync({ useFactory: async () => ({ gateway: { serviceList: [ /* services */ ], }, server: { context: ({ req }) => ({ jwt: req.headers.authorization, }), }, }), imports: [BuildServiceModule], inject: [GATEWAY_BUILD_SERVICE], }), ], }) export class AppModule {} ``` #### 代碼優先 首先向 User 實體添加一些額外的裝飾器。 ~~~ts import { Directive, Field, ID, ObjectType } from '@nestjs/graphql'; @ObjectType() @Directive('@key(fields: "id")') export class User { @Field((type) => ID) id: number; @Field() name: string; } ~~~ `Resolver` 提供了另一種名為 `resolveReference()` 的方法。 每當相關資源需要用戶實例時,此方法由 `Mercurius` 網關觸發。 稍后我們將在 `Posts` 服務中看到一個這樣的例子。 請注意,該方法必須使用 `@ResolveReference()` 裝飾器進行注釋。 ~~~ts import { Args, Query, Resolver, ResolveReference } from '@nestjs/graphql'; import { User } from './user.entity'; import { UsersService } from './users.service'; @Resolver((of) => User) export class UsersResolver { constructor(private usersService: UsersService) {} @Query((returns) => User) getUser(@Args('id') id: number): User { return this.usersService.findById(id); } @ResolveReference() resolveReference(reference: { __typename: string; id: number }): User { return this.usersService.findById(reference.id); } } ~~~ 最后,我們通過在配置對象中注冊傳遞 MercuriusFederationDriver 驅動程序的 GraphQLModule 來連接所有內容: ~~~typescript import { MercuriusFederationDriver, MercuriusFederationDriverConfig, } from '@nestjs/mercurius'; import { Module } from '@nestjs/common'; import { UsersResolver } from './users.resolver'; import { UsersService } from './users.service'; // Not included in this example @Module({ imports: [ GraphQLModule.forRoot<MercuriusFederationDriverConfig>({ driver: MercuriusFederationDriver, autoSchemaFile: true, federationMetadata: true, }), ], providers: [UsersResolver, UsersService], }) export class AppModule {} ~~~ ### 異步配置 聯合服務和網關模塊都支持使用同樣的 `forRootAsync` 異步初始化,相關文檔詳見[快速開始](/8/graphql?id=async-配置)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看