Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gateway throws error when federated schema contains Subscription type #426

Open
jsangilve opened this issue Sep 30, 2019 · 10 comments
Open

Gateway throws error when federated schema contains Subscription type #426

jsangilve opened this issue Sep 30, 2019 · 10 comments

Comments

@jsangilve
Copy link

@jsangilve jsangilve commented Sep 30, 2019

  • Package: @apollo/gateway (but the error is caused by @apollo/federation)
  • Version: since @apollo/federation v0.9.1
  • Last known working version: v0.6.x

When a federated service's schema contains a Subscription type and it's loaded by the Apollo Gateway, the following error is returned:

GraphQLSchemaValidationError: [accounts] Subscription -> `Subscription` is an extension type, but `Subscription` is not defined

I understand Subscriptions aren't supported by Federation yet, but having them in the downstream service shouldn't affect the creation of the federated graph.

While debugging the problem, I realized the Gateway was processing the Subscription as an extension type (the same way it does with Query/Mutation), but it's not adding an empty definition type for Subscription.

To reproduce it, just run the federation demo adding a Subscription to the accounts service:

const typeDefs = gql`
  extend type Query {
    me: User
  }

  type User @key(fields: "id") {
    id: ID!
    name: String
    username: String
  }

  type Subscription {
    somethingHappened(input: String!): String!
  }
`;

Fork including the change above: https://github.com/jsangilve/federation-demo/blob/wip/example_with_subscription/services/accounts/index.js

@abernix
Copy link
Member

@abernix abernix commented Oct 3, 2019

Thanks for opening apollographql/apollo-server#3358, but I've closed it until we can discuss it a bit more. I'm curious — given the fact that subscriptions aren't supported by Apollo Gateway — if you can provide more details on the use-case where this does more than move failure from one point to another.

Elaborating: Execution of a graph with subscriptions in it is still going to be problematic, and we don't suggest exposing downstream services on their own so it feels like this just moves a failure from an earlier point to a later point, which seems like it sets false expectations for a developer which might result in disappointment after they've already put time into it.

@jsangilve
Copy link
Author

@jsangilve jsangilve commented Oct 6, 2019

Hey @abernix, thanks for taking the time to review the PR and look into this issue.

We have been using Apollo Federation since version 0.6.x. Our architecture is composed by different micro-services exposing GraphQL APIs; these APIs are composed together by the Apollo Gateway.

That's our basic setup — probably the most common between teams adopting Apollo Federation — and it worked, until we needed to provide real-time updates to our clients. We liked the idea of providing clients with all the info they needed through a single GQL Schema. However, given that Apollo Gateway does not support subscriptions, we looked for alternatives.

There are many ways, but I wrote down 4 different ways to overcome this issue:

  1. Directly exposing downstream service(s) for subscriptions: as you mentioned, this is not ideal. The main issue here is that each subscription might be provided by a different service (because domain boundaries) and you don't want to expose your infrastructure to clients, so you probably still want a... Gateway :).
  2. Creating a separate endpoint for Subscriptions: use a separate Apollo Server to expose Subscriptions. The Subscriptions data still comes from the micro-services — aka downstream services — but instead of directly exposing GraphQL subscriptions, they just publish the data into Redis/Postgres/GooglePubsub or any channel compatible with the PubSubEngine implementations. The Apollo server listens a resolves subscriptions.
  3. Enriching the Apollo Gateway with Subcription resolvers: until version 0.6.x, the Federated Graph composed by Apollo Gateway included the subscriptions types, but they didn't work. If you added resolvers for GQL Subscriptions at the Gateway level i.e. to the Apollo Server running the Gateway, it worked. Data for the Subscriptions was still provided by downstream services but the WebSockets and PubSub was solely handle by the ApolloServer at gateway.
  4. Not using Subscriptions at all and provide real-time updated through a different channel.

We decided to try 3, because it allowed us to give a single GQL Schema to our clients, while keeping the responsibility of defining the schema to the downstream services. Furthermore, considering that subscription resolvers were really simple — just some basic filtering based on the input parameters — we dynamically generated the resolvers after calling gateway.load().

Last week I updated to the latest version of Apollo Gateway and found the problem described on this issue. Having Subscription types in the downstream services causes a GraphQLSchemaValidationError.

GraphQLSchemaValidationError: [accounts] Subscription -> `Subscription` is an extension 
type, but `Subscription` is not defined

The error is confusing because it doesn't give a clue about Subscription not being supported by Apollo Gateway. In fact, it's actually not fully related and that's why I open the PR.

IMHO, given that Apollo Gateway doesn't support subscriptions, I would have expected the following behaviour:

If subscriptions are set to false, as described in the docs (see below), Apollo Server would just remove any Subscription type from the Federated Graph.

// https://www.apollographql.com/docs/apollo-server/federation/implementing/
const server = new ApolloServer({  
  gateway,

  // Currently, subscriptions are enabled by default with Apollo Server, however,
  // subscriptions are not compatible with the gateway.  We hope to resolve this
  // limitation in future versions of Apollo Server.  Please reach out to us on
  // https://spectrum.chat/apollo/apollo-server if this is critical to your adoption!
  subscriptions: false,
});

Otherwise, the subscription type would be composed into the Federated graph and a warning explaining that Subscriptions won't work is logged.

Reasoning: It would be easier to adopt Apollo Gateway if teams using Apollo Server don't need to change anything on the existing services (unless they want to extend other service's types), and they should only know that subscriptions won't work at the Gateway (the warning would make this clear).

As explained above, there are alternative to get subscriptions working, but that's completely optional.

Once subscriptions are supported by Apollo Gateway, the warning is removed and teams using Apollo Gateway would need to decide whether they expose them through the gateway or not (subscriptions: false).

I created a Codesandbox forking James Baxley's gateway example. You will need to fork it and change something on index.js to trigger the reload and see the GraphQLSchemaValidationError in the terminal. I had to manually load the Gateway to avoid the error thrown when gateway is defined and subscription is not false.

https://codesandbox.io/s/federation-gateway-error-with-subscriptions-6s7bt

Edit: the GraphQLSchemaValidationError is thrown, regardless of Apollo Server initialized with gateway and subscription: false properties: https://codesandbox.io/s/federation-gateway-without-loading-manually-hwzbl

@aquiseb
Copy link

@aquiseb aquiseb commented Jan 28, 2020

I get this error without subscription. I'm at the first stage of debugging this so I can't provide much more info.

It only occurs in production (I can't reproduce locally yet), with pm2 running multiple instances in cluster mode.

'[tracker] Registration -> Registration is an extension type, but Registration is not defined in any service'

@Stradivario
Copy link

@Stradivario Stradivario commented Apr 29, 2020

Workaround for this issue is to expose _service query with sdl property only with Queries and Mutations without augmenting schema from @apollo/federation

import { GraphQLObjectType, GraphQLSchema, GraphQLString, printSchema } from 'graphql';

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
      _service: {
        type: new GraphQLObjectType({
          name: 'GraphqlFederation',
          fields: () => ({
            sdl: {
              type: GraphQLString,
            },
          }),
        }),
        resolve: () => ({
          sdl: printSchema(
            new GraphQLSchema({
              query: schema.getQueryType(),
              mutation: schema.getMutationType(),
            })
          ),
        }),
      },
    }),
  }),
});

With this approach you can have 1 Subscription server providing Pub/Sub capabilities and multiple Federated graphs. The client will subscribe to this one particular server and for queries and mutation you can use ApolloGateway.

Regards!

@abernix abernix transferred this issue from apollographql/apollo-server Jan 15, 2021
kawamataryo added a commit to kawamataryo/zenn-articles that referenced this issue Feb 6, 2021
diff --git a/.cache/kvs-node-localstorage/proofdict-lastUpdated b/.cache/kvs-node-localstorage/proofdict-lastUpdated
index 5c90088..2372ad4 100644
--- a/.cache/kvs-node-localstorage/proofdict-lastUpdated
+++ b/.cache/kvs-node-localstorage/proofdict-lastUpdated
@@ -1 +1 @@
-1612591652868
\ No newline at end of file
+1612650555629
\ No newline at end of file
diff --git a/articles/try-apollo-federation.md b/articles/try-apollo-federation.md
index 4ad282e..627e696 100644
--- a/articles/try-apollo-federation.md
+++ b/articles/try-apollo-federation.md
@@ -8,10 +8,29 @@ published: false

 # Apollo Federationとは?

-最近、Netflix の GraphQL アーキテクチャ構成が話題になりました。その構成を裏で支えているのが Apollo Federation です。
+最近、Netflix の GraphQL アーキテクチャ構成が話題になりました。その構成を支えているのが Apollo Federation です。

 https://netflixtechblog.com/how-netflix-scales-its-api-with-graphql-federation-part-1-ae3557c187e2

+Apollo Federation は、GraphQL の複数のマイクロサービスをゲートウェイでまとめて、クライアントからみるとまるで 1 つの GraphQL サーバーと通信しているかのように通信を処理するアーキテクチャです。
+これだけだとただリクエストとレスポンスの集約のように見えるのですが、それぞれののマイクロサービスで依存する処理も良い感じに統合してくれます。
+
+例えば、投稿情報は Post サービスにあり、その投稿の投稿者であるユーザーの情報は User サービスにあるときに、以下のような宣言で投稿情報と投稿者情報を取得したいとします。
+
+```graphql
+query {
+  posts {
+    id
+    title
+    content
+    user {
+      id
+      name
+    }
+  }
+}
+```
+

 # Demo
@@ -38,7 +57,7 @@ yarn add ts-node apollo-server @apollo/federation
 ```

 ### Usersサーバー
-最初にユーザー情報を管理する Users サーバーの実装はこちらです。
+ユーザー情報を管理する Users サーバーを作ります。

 ```
 users
@@ -119,11 +138,6 @@ server.listen({ port: 4002 }).then(({ url }) => {
 :::

 Federation のポイントは、type-defs.ts のスキーマ定義にある下記の部分です。
-オブジェクト型のスキーマ定義に`@key`ディレクティブを追加することで、エンティティとして指定できます。
-エンティティはゲートウェイを通して、他の GraphQL サーバーから参照できる型です。
-
-`(fields: "id")`の部分はエンティティの主キーの定義です。ゲートウェイサーバーではこの主キーを使って、識別をします。
-今回は User 型の id フィールドを指定しています。

 ```graphql
 type User @key(fields: "id") {
@@ -132,8 +146,14 @@ type User @key(fields: "id") {
 }
 ```

+オブジェクト型のスキーマ定義に`@key`ディレクティブを追加することで、エンティティとして指定できます。
+エンティティはゲートウェイを通して、他の GraphQL サーバーから参照できる型です。
+
+`(fields: "id")`の部分はエンティティの主キーの定義です。ゲートウェイサーバーではこの主キーを使って、識別をします。
+今回は User 型の id フィールドを指定しています。
+
+
 resolvers.ts では下記のコードで別 GraphQL サーバーからエンティティである User 型が参照された際の処理を書いています。
-この例では別 GraphQL サーバーから User が参照されたときに、メモリの構造体から対象の User を抽出して返しています。

 ```ts
 export const resolvers = {
@@ -147,9 +167,10 @@ export const resolvers = {
 }
 ```

+この例では別 GraphQL サーバーから User が参照されたときに、こちらのリゾルバが呼ばれインメモリのオブジェクトから対象の User を探して返しています。

 ### Postsサーバー
-続いて投稿情報を管理する Posts サーバーの実装はこちらです。
+投稿情報を管理する Posts サーバーを作ります。

 ```
 posts
@@ -240,11 +261,7 @@ server.listen({ port: 4001 }).then(({ url }) => {
 ```
 :::

-ポイントは、`type-defs.ts`の GraphQL 定義の以下の部分です。
-Post サーバーにはない User の情報を Post の型定義として使っています。そしてその参照先はに`extend type User`になっています。
-`extends` をつけるとその型は外部の GraphQL サーバーのエンティティに対する参照型となります。
-`@key(fields: "id")`ディレクティブがその型を参照する際の主キーです。そして、id に対して`@external`ディレクティブを付与して、このフィールドが外部の GraphQL サーバーにオリジナルがあることを示しています。
-
+ポイントは、`type-defs.ts`の GraphQL 定義の下記の部分です。

 ```graphql
   type Post {
@@ -261,8 +278,11 @@ Post サーバーにはない User の情報を Post の型定義として使っ
   }
 ```

-resolvers.ts 以下を定義しています。
-この関数で return するのは User 型を解決するために必要な情報です。`__typename`は User がある GraphQL サーバーの識別子、そして id は参照の主キーです。
+Post サーバーにはない User の情報を Post の型定義として使っています。そしてその参照先はに`extend type User`になっています。
+`extends` をつけるとその型は外部の GraphQL サーバーのエンティティに対する参照型となります。
+`@key(fields: "id")`ディレクティブがその型を参照する際の主キーです。そして、id に対して`@external`ディレクティブを付与して、このフィールドが外部の GraphQL サーバーにオリジナルがあることを示しています。
+
+resolvers.ts 下記を定義しています。

 ```ts
 export const resolvers = {
@@ -276,6 +296,8 @@ export const resolvers = {
 }
 ```

+この関数で return するのは User 型を解決するために必要な情報です。`__typename`は User がある GraphQL サーバーの識別子、そして id は参照の主キーです。
+
 ### マイクロサービスの起動

 Posts サーバーと Users サーバーを起動させるためのスクリプトを package.json に追記します。
@@ -295,13 +317,75 @@ Posts サーバーと Users サーバーを起動させるためのスクリプ
 以上でマイクロサービス側の実装は完了です。

 ### ゲートウェイ側の実装(API Routes)
-続いて Next.js に立てる GraphQL サーバー側の実装です。
+続いて Next.js の API Routes に立てる GraphQL サーバー(ゲートウェイ)を作ります。
+
+まず、Apollo Server の [micro](https://www.npmjs.com/package/micro) インテグレーションである[apollo-server-micro](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-server-micro)と、Apollo Federation のゲートウェイ側ライブラリである[@apollo/gateway](https://www.npmjs.com/package/@apollo/gateway)を依存に追加します。
+
+```bash
+yarn add @apollo/gateway apollo-server-micro
+```
+
+次に Next.js の pages/api 配下に`graphql.ts`を追加します。
+
+```
+src/pages/api
+└── graphql.ts
+```
+
+```ts:pages/api/graphql.ts
+import { ApolloServer } from 'apollo-server-micro';
+import { ApolloGateway } from '@apollo/gateway';
+
+export const config = {
+  api: {
+    bodyParser: false,
+  },
+};

-で動けば完了です。
+const gateway = new ApolloGateway({
+  serviceList: [
+    { name: 'posts', url: 'http://localhost:4001' },
+    { name: 'users', url: 'http://localhost:4002' },
+  ],
+});
+
+export default new ApolloServer({
+  gateway,
+  subscriptions: false,
+}).createHandler({
+  path: '/api/graphql',
+});
+```
+
+`ApolloGateway`の初期化時に、先程作成したモックのモックのマイクロサービスのエンドポイント情報を指定しています。
+そして`ApolloServer`の初期化時に ApolloGateway のインスタンスを渡します。
+また、この時に`subscriptions: false`を設定しています。これは 2021/02/06 現在 Apollo Gateway が GraphQL の Subscription と互換性がないためです([参考](apollographql/federation#426
+
+この状態で、`yarn dev`で Next.js を起動し、http://localhost:3000/api/graphql にアクセスすれば GraphiQL が起動するはずです。
+
+![](https://i.gyazo.com/a5d50361de6896f0bcc0f15143b00b93.png)
+
+しっかりそれぞれおのマイクロサービス側のクエリとミューテーションがスキーマに存在し、Post の User のように依存するクエリにも対応しています。
+
+```graphql
+query {
+  posts {
+    id
+    title
+    content
+    user {
+      id
+      name
+    }
+  }
+}
+```

-# 終わりに
+これで Apollo Federation の完成です。

-現実問題、API Routes に Gateway の Apollo Server を立てるのはパフィーマンス的に

 # 参考
-- [Entities - Apollo Federation - Apollo GraphQL Docs](https://www.apollographql.com/docs/federation/entities/)
\ No newline at end of file
+- [Entities - Apollo Federation - Apollo GraphQL Docs](https://www.apollographql.com/docs/federation/entities/)
+- [Apollo Federationのすゝめ -GraphQLとマイクロサービス- | Web系エンジニアのアウトプット練習場](https://blog.h-sakano.dev/posts/ixgnj6xg8)
+- [GraphQLでマイクロサービスアーキテクチャを構築する際に有効なApollo Federationを採用する際に注意すべきこと | Web系エンジニアのアウトプット練習場](https://blog.h-sakano.dev/posts/ybaupsbu8)
+- [GraphQLとマイクロサービスは相性が良さそうな件 〜Apollo Federationを用いたスキーママージについて〜 | スペースマーケットブログ](https://blog.spacemarket.com/code/graphql-apollo-federation/)
\ No newline at end of file
kawamataryo added a commit to kawamataryo/zenn-articles that referenced this issue Feb 7, 2021
diff --git a/.cache/kvs-node-localstorage/proofdict-lastUpdated b/.cache/kvs-node-localstorage/proofdict-lastUpdated
index 6b74032..46f54a2 100644
--- a/.cache/kvs-node-localstorage/proofdict-lastUpdated
+++ b/.cache/kvs-node-localstorage/proofdict-lastUpdated
@@ -1 +1 @@
-1612656229556
\ No newline at end of file
+1612697378091
\ No newline at end of file
diff --git a/articles/try-apollo-federation.md b/articles/try-apollo-federation.md
index 2bea60a..6f11ebd 100644
--- a/articles/try-apollo-federation.md
+++ b/articles/try-apollo-federation.md
@@ -1,21 +1,25 @@
 ---
-title: "Next.jsのAPI RoutesでApollo Federationを試す"
+title: "Apollo Federation で GraphQLマイクロサービスアーキテクチャを構築する"
 emoji: "🇫🇲"
 type: "tech" # tech: 技術記事 / idea: アイデア
 topics: ["apollo", "graphql", "typescript"]
 published: false
 ---

+最近調べていた Apollo Federation についてのメモです。
+Apollo Federation の概要と、Next.js の API Routes で Apollo Federation を使う例をまとめます。
+
 # Apollo Federationとは?

-最近、Netflix の GraphQL アーキテクチャ構成が話題になりました。その構成を支えているのが Apollo Federation です。
+以前 Netflix の GraphQL マイクロサービスアーキテクチャが話題になりました。その構成を支えているのが Apollo Federation です。

 https://netflixtechblog.com/how-netflix-scales-its-api-with-graphql-federation-part-1-ae3557c187e2

-Apollo Federation は、複数の GraphQL マイクロサービスをゲートウェイでまとめて、1 つの GraphQL サーバーとして提供するためのアーキテクチャです。
-これだけだとただのゲートウェイでの集約のように見えるのですが、Apollo Federation を使うことでそれぞれのマイクロサービスで依存する処理も良い感じに統合してくれます。
+Apollo Federation は、複数の GraphQL マイクロサービスをゲートウェイでまとめて、1 つの GraphQL エンドポイントとして提供するものです。
+Apollo Federation を使うことでそれぞれのマイクロサービ間で依存する処理を良い感じに統合してくれます。

-例えば、投稿情報は Post サービスにあり、その投稿の投稿者であるユーザーの情報は User サービスにあるときに、以下のようなクエリで投稿情報と投稿者情報を取得したいとします。
+例えば、投稿情報は Post マイクロサービスにあり、その投稿の投稿者であるユーザーの情報は User マイクロサービスにあるとします。
+その時に以下のようなクエリで投稿情報と、その投稿のユーザー情報をまとめて取得したいユースケースがあったとします。

 ```graphql
 query {
@@ -31,23 +35,25 @@ query {
 }
 ```

-この時に Apollo Federation を使わない場合は、以下のように投稿情報を処理する Post サーバーからユーザー情報を処理する User サーバーにクエリを投げる必要があり、マイクロサービスごとが密結合になってしまいます。
+Apollo Federation を使わない場合は、リクエストを返すために投稿情報を処理する Post マイクロサービスからユーザー情報を処理する User マイクロサービスにクエリを投げる必要があり、マイクロサービス同士が密結合になってしまいます。

 ![](https://i.gyazo.com/98d841301f82c3f4092733be8545ba77.png)

-それを Apollo Federation を使うとゲートウェイ側良い感じにハンドリングしてくれて、マイクロサービス間の結合度を下げられます。
+それを Apollo Federation を使うとゲートウェイ側で依存するデータ処理をハンドリングしてくれるので、各マイクロサービス間の結合度を下げられます。

 ![](https://i.gyazo.com/eaf71bde17a4d27692129656b8e6a503.png)
-# Demo

-サンプルアプリを作ります。
-Next.js の環境構築と TypeScript 化が終わっている前提で書きます。
+# Apollo Federation を使った実装

-構成はこちらです。Next.js の API Routes にゲートウェイサーバーを立て、その後ろに 2 つの GraphQL サーバーを作ります。
+Apollo Federation を使ったサンプルアプリを作ります。
+Next.js の API Routes に Apollo Federation のゲートウェイを立てて、その裏に投稿情報を処理する Post サーバーと、ユーザー情報を処理する User サーバーを立てる構成です。

+![](https://i.gyazo.com/fab4064441a19d70319bae9c43b39102.png)
+
+Next.js の環境構築と TypeScript 化が終わっている前提で書きます。
 ## マイクロサービス側の実装

-まずモックのマイクロサービス用ディレクトリを作ります。
+Next.js のプロジェクトルートにマイクロサービス用ディレクトリを作ります。

 ```bash
 mkdir -p servers
@@ -61,9 +67,10 @@ mkdir servers/users # ユーザー情報の処理サーバー
 yarn add ts-node apollo-server @apollo/federation
 ```

-### Usersサーバー
+### Users サーバー
 ユーザー情報を管理する Users サーバーを作ります。

+
 ```
 users
 ├── index.ts
@@ -71,7 +78,8 @@ users
 └── type-defs.ts
 ```

-:::details servers/users/type-defs.ts
+:::details servers/users/type-defs
+
 ```ts:servers/users/type-defs
 import { gql } from 'apollo-server';

@@ -94,7 +102,8 @@ export const typeDefs = gql`
 :::

 :::details servers/users/resolvers.ts
-```ts:
+
+```ts:servers/users/resolvers.ts
 export const DB = {
   users: [
     { id: 1, name: 'taro' },
@@ -126,6 +135,7 @@ export const resolvers = {
 :::

 :::details servers/users/index.ts
+
 ```ts:servers/users/index.ts
 import { ApolloServer } from 'apollo-server';
 import { typeDefs } from './type-defs';
@@ -140,9 +150,10 @@ server.listen({ port: 4002 }).then(({ url }) => {
   console.log(`🚀  Server ready at ${url}`);
 });
 ```
+
 :::

-Federation のポイントは、type-defs.ts のスキーマ定義にある下記の部分です。
+ポイントは、type-defs.ts のスキーマ定義にある下記の部分です。

 ```graphql
 type User @key(fields: "id") {
@@ -151,11 +162,9 @@ type User @key(fields: "id") {
 }
 ```

-オブジェクト型のスキーマ定義に`@key`ディレクティブを追加することで、エンティティとして指定できます。
-エンティティはゲートウェイを通して、他の GraphQL サーバーから参照できる型です。
+オブジェクト型のスキーマ定義に`@key`ディレクティブを追加することで、エンティティとして指定できます。エンティティはゲートウェイを通して、他の GraphQL サーバーから参照できる特殊な型です。

-`(fields: "id")`の部分はエンティティの主キーの定義です。ゲートウェイサーバーではこの主キーを使って、識別をします。
-今回は User 型の id フィールドを指定しています。
+`(fields: "id")`の部分はエンティティの主キーの定義です。ゲートウェイサーバーではこの主キーを使って、データを識別をします。今回は User 型の id フィールドを指定しています。

 resolvers.ts では下記のコードで別 GraphQL サーバーからエンティティである User 型が参照された際の処理を書いています。
@@ -175,7 +184,7 @@ export const resolvers = {
 この例では別 GraphQL サーバーから User が参照されたときに、こちらのリゾルバが呼ばれインメモリのオブジェクトから対象の User を探して返しています。

 ### Postsサーバー
-投稿情報を管理する Posts サーバーを作ります。
+投稿情報を管理するマイクロサービス Posts サーバーを作ります。

 ```
 posts
@@ -185,6 +194,7 @@ posts
 ```

 :::details servers/posts/type-defs.ts
+
 ```ts:servers/posts/type-defs.ts
 import { gql } from 'apollo-server';

@@ -214,9 +224,11 @@ export const typeDefs = gql`
   }
 `;
 ```
+
 :::

 :::details servers/posts/resolvers.ts
+
 ```ts:servers/posts/resolvers.ts
 const DB = {
   posts: [
@@ -247,9 +259,11 @@ export const resolvers = {
   },
 };
 ```
+
 :::

 :::details servers/posts/index.ts
+
 ```ts:servers/posts/index.ts
 import { ApolloServer } from 'apollo-server';
 import { typeDefs } from './type-defs';
@@ -264,6 +278,7 @@ server.listen({ port: 4001 }).then(({ url }) => {
   console.log(`🚀  Server ready at ${url}`);
 });
 ```
+
 :::

 ポイントは、`type-defs.ts`の GraphQL 定義の下記の部分です。
@@ -321,8 +336,8 @@ Posts サーバーと Users サーバーを起動させるためのスクリプ

 以上でマイクロサービス側の実装は完了です。

-### ゲートウェイ側の実装(API Routes)
-続いて Next.js の API Routes に立てる GraphQL サーバー(ゲートウェイ)を作ります。
+### ゲートウェイ側の実装(Next.js API Routes)
+続いて Next.js の API Routes に GraphQL サーバー(ゲートウェイ)を立てます。

 まず、Apollo Server の [micro](https://www.npmjs.com/package/micro) インテグレーションである[apollo-server-micro](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-server-micro)と、Apollo Federation のゲートウェイ側ライブラリである[@apollo/gateway](https://www.npmjs.com/package/@apollo/gateway)を依存に追加します。

@@ -367,27 +382,15 @@ export default new ApolloServer({
 また、この時に`subscriptions: false`を設定しています。これは 2021/02/06 現在 Apollo Gateway が GraphQL の Subscription と互換性がないためです([参考](apollographql/federation#426

 この状態で、`yarn dev`で Next.js を起動し、http://localhost:3000/api/graphql にアクセスすれば GraphiQL が起動するはずです。
+GraphQL Docs を見るとそれぞれのマイクロサービス側のクエリとミューテーションがスキーマに存在し、Post にネストされた User のように依存するクエリにも対応しています。

-![](https://i.gyazo.com/a5d50361de6896f0bcc0f15143b00b93.png)
+![](https://i.gyazo.com/c532060caebaa514bb5156ca361542d3.png)

-しっかりそれぞれおのマイクロサービス側のクエリとミューテーションがスキーマに存在し、Post の User のように依存するクエリにも対応しています。
-
-```graphql
-query {
-  posts {
-    id
-    title
-    content
-    user {
-      id
-      name
-    }
-  }
-}
-```
+これで Apollo Federation での GraphQL マイクロサービスアーキテクチャの完成です 🌮

-これで Apollo Federation の完成です。
+フロント側も実装したサンプルアプリが以下リポジトリにあるので参考までに。

+https://github.com/kawamataryo/sandbox-for-nextjs-and-apollo-server

 # 参考
 - [Entities - Apollo Federation - Apollo GraphQL Docs](https://www.apollographql.com/docs/federation/entities/)
kawamataryo added a commit to kawamataryo/zenn-articles that referenced this issue Feb 7, 2021
diff --git a/.cache/kvs-node-localstorage/proofdict-lastUpdated b/.cache/kvs-node-localstorage/proofdict-lastUpdated
index 678d964..325634f 100644
--- a/.cache/kvs-node-localstorage/proofdict-lastUpdated
+++ b/.cache/kvs-node-localstorage/proofdict-lastUpdated
@@ -1 +1 @@
-1612725471231
\ No newline at end of file
+1612727400620
\ No newline at end of file
diff --git a/articles/try-apollo-federation.md b/articles/try-apollo-federation.md
index 8256b17..ffa42e4 100644
--- a/articles/try-apollo-federation.md
+++ b/articles/try-apollo-federation.md
@@ -7,7 +7,7 @@ published: true
 ---

 最近調べていた Apollo Federation についてのメモです。
-Apollo Federation の概要と、Next.js の API Routes で Apollo Federation を使う例をまとめます。
+Apollo Federation の概要と、Next.js の API Routes で Apollo Federation を使うサンプルアプリの構築をまとめています。

 # Apollo Federationとは?

@@ -16,7 +16,7 @@ Apollo Federation の概要と、Next.js の API Routes で Apollo Federation
 https://netflixtechblog.com/how-netflix-scales-its-api-with-graphql-federation-part-1-ae3557c187e2

 Apollo Federation は、複数の GraphQL マイクロサービスをゲートウェイでまとめて、1 つの GraphQL エンドポイントとして提供するものです。
-Apollo Federation を使うことでそれぞれのマイクロサービ間で依存する処理を良い感じに統合してくれます。
+Apollo Federation を使うことでそれぞれのマイクロサービス間で依存する処理を良い感じに統合してくれます。

 例えば、投稿情報は Post マイクロサービスにあり、その投稿の投稿者であるユーザーの情報は User マイクロサービスにあるとします。
 その時に以下のようなクエリで投稿情報と、その投稿のユーザー情報をまとめて取得したいユースケースがあったとします。
@@ -298,9 +298,9 @@ server.listen({ port: 4001 }).then(({ url }) => {
   }
 ```

-Post サーバーにはない User の情報を Post の型定義として使っています。そしてその参照先はに`extend type User`になっています。
+Post サーバーにはない User の情報を Post の型定義として使っています。そしてその参照先は`extend type User`になっています。
 `extends` をつけるとその型は外部の GraphQL サーバーのエンティティに対する参照型となります。
-`@key(fields: "id")`ディレクティブがその型を参照する際の主キーです。そして、id に対して`@external`ディレクティブを付与して、このフィールドが外部の GraphQL サーバーにオリジナルがあることを示しています。
+`@key(fields: "id")`ディレクティブがその型を参照する際の主キーです。id に対して`@external`ディレクティブを付与して、このフィールドが外部の GraphQL サーバーにオリジナルがあることを示しています。

 resolvers.ts 下記を定義しています。

@@ -316,7 +316,7 @@ export const resolvers = {
 }
 ```

-この関数で return するのは User 型を解決するために必要な情報です。`__typename`は User がある GraphQL サーバーの識別子、そして id は参照の主キーです。
+この関数で return するのは User 型を解決するために必要な情報です。`__typename`は User がある GraphQL サーバーの識別子 で id は参照先エンティティの主キーです。

 ### マイクロサービスの起動

@@ -337,7 +337,7 @@ Posts サーバーと Users サーバーを起動させるためのスクリプ
 以上でマイクロサービス側の実装は完了です。

 ### ゲートウェイ側の実装(Next.js API Routes)
-続いて Next.js の API Routes に GraphQL サーバー(ゲートウェイ)を立てます。
+Next.js の API Routes に GraphQL サーバー(ゲートウェイ)を立てます。

 まず、Apollo Server の [micro](https://www.npmjs.com/package/micro) インテグレーションである[apollo-server-micro](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-server-micro)と、Apollo Federation のゲートウェイ側ライブラリである[@apollo/gateway](https://www.npmjs.com/package/@apollo/gateway)を依存に追加します。

@@ -377,11 +377,23 @@ export default new ApolloServer({
 });
 ```

-`ApolloGateway`の初期化時に、先程作成したモックのモックのマイクロサービスのエンドポイント情報を指定しています。
-そして`ApolloServer`の初期化時に ApolloGateway のインスタンスを渡します。
+`ApolloGateway`の初期化時に、Apollo Federation で扱う GraphQL マイクロサービスを指定します。この例では先程作成した Users サーバーと Posts サーバーのエンドポイント情報を設定します。
+そして`ApolloServer`の初期化時に `ApolloGateway` のインスタンスを渡します。
 また、この時に`subscriptions: false`を設定しています。これは 2021/02/06 現在 Apollo Gateway が GraphQL の Subscription と互換性がないためです([参考](apollographql/federation#426

-この状態で、`yarn dev`で Next.js を起動し、http://localhost:3000/api/graphql にアクセスすれば GraphiQL が起動するはずです。
+以上でゲートウェイ側の実装は完了です。とてもシンプルですね。
+
+### ゲートウェイの起動
+
+Next.js を起動し、GraphiQL にアクセスしてみましょう。
+
+※ このコマンド前に`yarn dev:server:posts`と`yarn dev:server:users`でモックのマイクロサービスを起動しておいてください。
+
+```bash
+yarn dev
+```
+
+http://localhost:3000/api/graphql にアクセスすれば GraphiQL が起動します。
 GraphQL Docs を見るとそれぞれのマイクロサービス側のクエリとミューテーションがスキーマに存在し、Post にネストされた User のように依存するクエリにも対応しています。

 ![](https://i.gyazo.com/c532060caebaa514bb5156ca361542d3.png)
kawamataryo added a commit to kawamataryo/zenn-articles that referenced this issue Feb 7, 2021
diff --git a/.cache/kvs-node-localstorage/proofdict-lastUpdated b/.cache/kvs-node-localstorage/proofdict-lastUpdated
index 370d97d..a90db55 100644
--- a/.cache/kvs-node-localstorage/proofdict-lastUpdated
+++ b/.cache/kvs-node-localstorage/proofdict-lastUpdated
@@ -1 +1 @@
-1612728316206
\ No newline at end of file
+1612739965607
\ No newline at end of file
diff --git a/articles/try-apollo-federation.md b/articles/try-apollo-federation.md
index d2c632e..80d493e 100644
--- a/articles/try-apollo-federation.md
+++ b/articles/try-apollo-federation.md
@@ -16,7 +16,7 @@ Apollo Federation の概要と、Next.js の API Routes で Apollo Federation
 https://netflixtechblog.com/how-netflix-scales-its-api-with-graphql-federation-part-1-ae3557c187e2

 Apollo Federation は、複数の GraphQL マイクロサービスをゲートウェイでまとめて、1 つの GraphQL エンドポイントとして提供するものです。
-Apollo Federation を使うことでそれぞれのマイクロサービス間で依存する処理を良い感じに統合してくれます。
+Apollo Federation を使うことで、それぞれのマイクロサービス間で依存する処理を良い感じに統合してくれます。

 例えば、投稿情報は Post マイクロサービスにあり、その投稿の投稿者であるユーザーの情報は User マイクロサービスにあるとします。
 その時に以下のようなクエリで投稿情報と、その投稿のユーザー情報をまとめて取得したいユースケースがあったとします。
@@ -56,8 +56,7 @@ Next.js の環境構築と TypeScript 化が終わっている前提で書きま
 Next.js のプロジェクトルートにマイクロサービス用ディレクトリを作ります。

 ```bash
-mkdir -p servers
-mkdir servers/posts # 投稿者情報の処理サーバー
+mkdir -p servers/posts # 投稿者情報の処理サーバー
 mkdir servers/users # ユーザー情報の処理サーバー
 ```

@@ -164,10 +163,11 @@ type User @key(fields: "id") {

 オブジェクト型のスキーマ定義に`@key`ディレクティブを追加することで、エンティティとして指定できます。エンティティはゲートウェイを通して、他の GraphQL サーバーから参照できる特殊な型です。

-`(fields: "id")`の部分はエンティティの主キーの定義です。ゲートウェイサーバーではこの主キーを使って、データを識別をします。今回は User 型の id フィールドを指定しています。
+`(fields: "id")`の部分はエンティティの主キーの定義です。参照先ではこの主キーを使って、データを指定します。今回は User 型の id フィールドを設定しています。

 resolvers.ts では下記のコードで別 GraphQL サーバーからエンティティである User 型が参照された際の処理を書いています。
+別 GraphQL サーバーから User が参照されたときに、このリゾルバが呼ばれインメモリのオブジェクトから対象の User を探して返しています。

 ```ts
 export const resolvers = {
@@ -181,10 +181,8 @@ export const resolvers = {
 }
 ```

-この例では別 GraphQL サーバーから User が参照されたときに、こちらのリゾルバが呼ばれインメモリのオブジェクトから対象の User を探して返しています。
-
 ### Postsサーバー
-投稿情報を管理するマイクロサービス Posts サーバーを作ります。
+投稿情報を管理する Posts サーバーを作ります。

 ```
 posts
@@ -299,10 +297,10 @@ server.listen({ port: 4001 }).then(({ url }) => {
 ```

 Post サーバーにはない User の情報を Post の型定義として使っています。そしてその参照先は`extend type User`になっています。
-`extends` をつけるとその型は外部の GraphQL サーバーのエンティティに対する参照型となります。
+`extends` をつけると、その型は外部の GraphQL サーバーのエンティティに対する参照型となります。
 `@key(fields: "id")`ディレクティブがその型を参照する際の主キーです。id に対して`@external`ディレクティブを付与して、このフィールドが外部の GraphQL サーバーにオリジナルがあることを示しています。

-resolvers.ts 下記を定義しています。
+resolvers.ts では下記を定義しています。

 ```ts
 export const resolvers = {
@@ -316,7 +314,7 @@ export const resolvers = {
 }
 ```

-この関数で return するのは User 型を解決するために必要な情報です。`__typename`は User がある GraphQL サーバーの識別子 で id は参照先エンティティの主キーです。
+この関数で return するのは User 型を解決するために必要な情報です。`__typename` は User がある GraphQL サーバーの識別子 で id は参照先エンティティの主キーです。

 ### マイクロサービスの起動

@@ -330,7 +328,10 @@ Posts サーバーと Users サーバーを起動させるためのスクリプ
   },
 ```

-これで、`yarn dev:server:posts`, ``yarn dev:server:users`コマンドでマイクロサービスの GraphQL サーバーが起動します。
+これで、`yarn dev:server:posts`, ``yarn dev:server:users`コマンドで各マイクロサービスの GraphQL サーバーが起動します。
+
+posts サーバー: http://localhost:4001
+users サーバー: http://localhost:4002

 ![](https://i.gyazo.com/853d69698467c56270f4d318d7aa9db8.png)

@@ -377,7 +378,7 @@ export default new ApolloServer({
 });
 ```

-`ApolloGateway`の初期化時に、Apollo Federation で扱う GraphQL マイクロサービスを指定します。この例では先程作成した Users サーバーと Posts サーバーのエンドポイント情報を設定します。
+`ApolloGateway`の初期化時に、Apollo Federation で扱う GraphQL マイクロサービスを配列で指定します。この例では先程作成した Users サーバーと Posts サーバーのエンドポイント情報を設定します。
 そして`ApolloServer`の初期化時に `ApolloGateway` のインスタンスを渡します。
 また、この時に`subscriptions: false`を設定しています。これは 2021/02/06 現在 Apollo Gateway が GraphQL の Subscription と互換性がないためです([参考](apollographql/federation#426

@@ -386,8 +387,7 @@ export default new ApolloServer({
 ### ゲートウェイの起動

 Next.js を起動し、GraphiQL にアクセスしてみましょう。
-
-※ このコマンド前に`yarn dev:server:posts`と`yarn dev:server:users`でモックのマイクロサービスを起動しておいてください。
+※ このコマンド前に`yarn dev:server:posts`と`yarn dev:server:users`で各マイクロサービスを起動しておいてください。

 ```bash
 yarn dev
@@ -406,7 +406,7 @@ https://github.com/kawamataryo/sandbox-for-nextjs-and-apollo-server

 # 終わりに

-ここまでまとめたサンプルアプリは Apollo Federation の一部の機能しかつかっていません。
+この記事では Apollo Federation の一部の機能しか使っていません。
 他に色々便利な機能があるので、是非公式ドキュメントを一読ください。

 https://www.apollographql.com/docs/federation/
@tot-ra
Copy link

@tot-ra tot-ra commented Jun 30, 2021

We encountered same issue with Subscriptions.

@abernix our use-case is that we use federation for queries & mutations, while subscriptions are served by separate endpoint. But we still want to serve combined schema to the playground (graphiql)

So we try to run composeAndValidate() with schemas from all services and get error above

@abernix
Copy link
Member

@abernix abernix commented Jul 16, 2021

@tot-ra We understand and we continue to investigate ways to improve the options we offer for employing Subscriptions and taking advantage of Federation. Today, our only concrete suggestion is offered in this blog post.

@tot-ra
Copy link

@tot-ra tot-ra commented Sep 7, 2021

@abernix yes, we've implemented same thing Mandi is doing. It doesn't solve the problem with schema validation though, because she is not running composeAndValidate. She is just merging gateway schema with built-in subscriptions https://github.com/apollosolutions/federation-subscription-tools/blob/main/src/utils/schema.js#L27

What we want is to validate & register subscription schema in our schema-registry - for audit, naming collisions & codestyle checks. But since we rely on composeAndValidate it blocks us.

https://github.com/pipedrive/graphql-schema-registry/blob/be85c81ca8a3c2ab56be036cd6b91dde8d97dd27/app/helpers/federation.js#L24

@michjak-szymanski
Copy link

@michjak-szymanski michjak-szymanski commented Nov 30, 2021

Hi.
We are using Managed federation and we also need Subscriptions in our federated graphs. I've managed to implement them on our Apollo Gateway by following this tutorial https://www.apollographql.com/blog/backend/federation/using-subscriptions-with-your-federated-data-graph/ and it works.

However the problem is in our CI/CD pipeline we cannot publish/check a subgraph that contains Subscription type into Apollo Studio (registry). Previously we were dynamically removing Subscription type from schema we were about to check/publish in our CI/CD, it was lame but it worked... But since now we actually need to have Subscription in our schema we cannot do it anymore and we are stuck with following error we get from Rover:

Checking the proposed schema for subgraph ident against *****
error[E029]: Encountered 1 build error while trying to build subgraph "*" into supergraph "*****@production".

Caused by:
    Encountered 1 build error while trying to build the supergraph.
    EXTENSION_WITH_NO_BASE: [ident] Subscription -> `Subscription` is an extension type, but `Subscription` is not defined in any service
    
        The changes in the schema you proposed for subgraph ident are incompatible with supergraph *****@production. See https://www.apollographql.com/docs/federation/errors/ for more information on resolving build errors.

I've investigated the Rover code and it seems the error originates from https://graphql.api.apollographql.com/api/graphql which is beyond our control :)

Is there a way to bypass this error while still being able to have Subscription in federated schema on Apollo Studio?

@sammysaglam
Copy link

@sammysaglam sammysaglam commented Jan 20, 2022

having waited for subscriptions support on federation for a while now , i was instead able to put together a small library implementing federation with subscriptions (the way we'd hoped for, without a separate service just for subscriptions). was able to get it working using schema-stitching & graphql-tools, it's basically just transforming federation SDL into schema-stitching SDL and using websockets across the gateway & subgraphs:
https://github.com/sammysaglam/federation-with-subscriptions

hope that helps

@jukben
Copy link

@jukben jukben commented Apr 21, 2022

I just second to @tot-ra we have exactly the same use case with the difference we use Rover / Apollo Studio.

What we want is to validate & register subscription schema in our schema registry.

Question to you @tot-ra, have you been able to overcome it in your solution? Looks pretty dope! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants