Apollo Federationとは?
過去記事にApollo Federation(以下、単にFederationとも)の紹介記事があるので、Federationが何かご存じない方は御覧ください!
Apollo Federationのすゝめ -GraphQLとマイクロサービス-
Apollo Federationを採用する際の注意点
上記の記事のように、Apollo FederationはGraphQLでマイクロサービスを構築する際にとても有効な選択肢のうちの1つですが、採用にあたって注意すべき点もあります。
それはずばり、「extend
して追加したフィールドに引数を指定することはできない」ということです。
ちょっとこれだけではわかりにくいと思うので、以下のような構成のブログサービスの例で考えてみましょう。
ブログサービスの例
ブログサービスの構成
ユーザー管理サービスとブログ管理サービスがそれぞれ定義したスキーマをFederation Gatewayで統合してユーザーに単一のGraphQL APIを提供します。
ユーザー管理サービスのスキーマ定義
まずは、ユーザー管理サービスから、ユーザー情報をGraphQLで取り扱うことができるように、以下のようなスキーマを定義します
extend type Query {
users: [User]
user(id: ID!): User
}
type User {
id: ID!
email: String!
firstName: String!
lastName: String!
nickname: String
}
このスキーマに対してリゾルバを定義すると、ユーザー情報の一覧や、id
を指定することで特定のユーザー情報を取得することができるようになるでしょう。
記事管理サービスのスキーマ定義
そして同様に、記事管理サービスにも記事に関するスキーマを定義します。
extend type Query {
posts: [Post]
post(id: ID!): Post
}
type Post {
id: ID!
title: String!
body: String!
authorId: ID!
}
サービス間のデータ参照
さて、ここでユーザーごとに記事一覧を取得できるようになれば便利です。
記事の管理は記事管理サービスの責務ですから、記事管理サービスでUserタイプを拡張します。
extend type User @key(fields: "id") {
id: ID! @external
posts: [Post]
}
問題が起きるのはここからです。
ユーザー1人あたりの記事の投稿数が多い場合、ページネーションを実装したり、記事の並び順を変更したりするため、上記のposts
フィールドに以下のように引数を取ることができれば便利です。
例えば、以下のような感じです。
extend type User @key(fields: "id") {
id: ID! @external
posts(after: String, before: String, first: Int, last: Int, order: String, orderBy: String): [Post]
}
ただ、上記のように、Apollo Federationを利用してextend
で追加したフィールドに引数を指定することは現状はできません。
ソース: Federation relation filtering and arguments problems (federation design concerns / feature request) · Issue #3420 · apollographql/apollo-server · GitHub
2つの妥協策
妥協策としては、2通り考えられます。
フィールドを分ける
ソート順程度のパターン数がある程度限られているものであれば、以下のようにソート順ごとに別のフィールドを用意することもできるでしょう。
extend type User @key(fields: "id") {
id: ID! @external
postsOrderedById: [Post]
postsOrderedByTitle: [Post]
}
柔軟性には欠けますが、引数の組み合わせに限りがある場合は有効な手ではあります。
extend
を諦める
ただ、ページネーションのようにパターン数が膨大な場合は、extend
してフィールドを追加することを諦めて、別の方法でユーザーごとの記事一覧を取得するしかありません。
具体的には、ルートクエリに定義したposts
フィールドでユーザー検索ができるように、author
引数を指定します。
extend type Query {
# authorで特定ユーザーによる記事を検索できるようにする
posts(after: String, before: String, first: Int, last: Int, author: ID!): [Post]
}
この場合、複数ユーザーの記事一覧を1回のクエリで取得できず、ユーザーごとにAPIを叩く必要があります。
また、以下のようにauthors
を引数で指定できるようにし、複数ユーザーの記事を一気に取ってきて、フロント側で表示を出し分けするという方法も考えられるかもしれません。
extend type Query {
# authorsで特定ユーザーによる記事を検索できるようにする
posts(after: String, before: String, first: Int, last: Int, authosr: [ID!]): [Post]
}
まとめ
以下の条件ならびに妥協策を許容できない場合、現状はApollo Federationを採用することはできませんのでご注意ください。
extend
して追加したフィールドに引数を指定することはできない- 妥協策1: 引数の組み合わせが少ない場合は、組み合わせごとにフィールドを定義する
- 妥協策2:
extend
を諦めて、ルートクエリで検索できるようにする