TL;DR
Apollo が開発した、GraphQL サーバを実装するための OSS ライブラリ。GraphQL Schema と Resolver を渡すだけでサーバを立ち上げられる。
仕組み
Resolver の定義
次のような GraphQL Schema を例にする。
type PostTag {
name: String!
}
type Post {
id: ID!
title: String!
body: String!
tags: [PostTag]!
}
interface User {
name: String!
}
type Author implements User {
name: String!
posts: [Post]!
}
type Query {
authors: [Author!]!
}Resolver は parent、args、context、info の 4 つの引数を受け取る関数である。
parent: 親の Resolver の戻り値。ルートの Resolver の直下では空になるargs: GraphQL Schema で定義したフィールドの引数context: GraphQL の操作全体で共有されるオブジェクト。DB コネクションやユーザの認証情報など、任意のデータを持たせられるinfo: 実行される操作の詳細。デバッグやエラー通知のメッセージを作るときに使う
各フィールドで返す値は、GraphQL Schema で定義した型と一致させる必要がある。
const resolvers = {
Query: {
authors: () => Object.entries(db.users).map(([id, user]) => ({ id, ...user })),
},
Post: {
tags: ({ tags }) => tags.map((name) => ({ name })),
},
Author: {
posts: ({ id }) =>
Object.entries(db.blogs)
.filter(([_, post]) => post.author === id)
.map(([id, post]) => ({ id, ...post })),
},
}Default resolvers
Resolver を定義していないフィールドには、Apollo Server が Default resolvers を適用する。これは、親のオブジェクトがフィールド名と同じプロパティを持っていれば、その値を返す。そのため Post.title や Author.name は、Resolver を定義しなくても期待どおりの値を返す。
サーバの起動
startStandaloneServer を使うと、簡単にサーバを立ち上げられる。下の例では、Resolver を追加した GraphQL Schema を ApolloServer に渡している。Schema ファイルの読み込み方は GraphQL Schema ファイルを TypeScript で読み込む にまとめた。
import { addResolversToSchema } from "@graphql-tools/schema"
import { ApolloServer } from "@apollo/server"
import { startStandaloneServer } from "@apollo/server/standalone"
const schemaWithResolvers = addResolversToSchema({ schema, resolvers })
const apolloServer = new ApolloServer({ schema: schemaWithResolvers })
const { url } = await startStandaloneServer(apolloServer)
console.log(url)表示された url を開くと、Apollo Sandbox というプレイグラウンド環境が起動する。
Resolver chains
Resolver chains は、ルートの Resolver から GraphQL Schema の最下層の Resolver まで処理が連鎖する仕組みと、その実行順序のことである。順序はパーサが生成する AST をもとに自動で決まるので、クライアントが指定したクエリを正しい順序で取得して返せる。生成される AST は AST explorer で確かめられる。
また、GraphQL サーバは AST と GraphQL Schema を比べて、クエリが有効かを確かめる。無効なクエリには、Resolver を実行せずにエラーを返す。
例
次のクエリでは、Resolver が一直線に実行される。
query GetPosts {
authors {
posts {
tags {
name
}
}
}
}flowchart LR Query.authors --> Author.posts --> Post.tags --> PostTag.name
複数のフィールドを取得すると、チェーンが分岐する。分岐したそれぞれのサブチェーンの Resolver は並列に実行される。
query GetPosts {
authors {
name
posts {
title
body
tags {
name
}
}
}
}flowchart LR Query.authors --> Author.name Query.authors --> Author.posts Author.posts --> Post.tags --> PostTag.name Author.posts --> Post.title Author.posts --> Post.body