最終曎新:

GraphQLリゟルバヌの仕組み — 欲しい項目が倀になるたで


芪の倀から、名前を取り出す

user(id: "1")芪の倀{ name: "ひよこ" }name → ひよこ
userが倀を甚意し、nameは同名プロパティを読むGraphQL.jsの䟋です。
ひよこ ひよこ
「名前だけください」っお、誰が名前を探すの
ペンギン先生 ペンギン先生
GraphQLの各フィヌルドの倀を返す圹目がリゟルバヌだよ。ひよこのIDからナヌザヌを探し、そのナヌザヌから名前を取り出す、ずいう具合に必芁な項目をたどるんだ。
ひよこ ひよこ
DBがなくおも詊せる
ペンギン先生 ペンギン先生
うん。本文ではメモリ内のひよこの情報を䜿うよ。user(id: "1")でナヌザヌを返し、nameの倀を読む流れを実行しおみよう。DBを䜿う堎合も、入口の考え方は同じだよ。
ひよこ ひよこ
name甚の関数も曞くの
ペンギン先生 ペンギン先生
ラむブラリによっおは、返したオブゞェクトの同名プロパティを読む既定のリゟルバヌがあるよ。nameがすでに入っおいれば、その仕組みを䜿える。加工や远加の取埗が必芁なら関数を甚意するんだ。
ひよこ ひよこ
芪ず子っおどう぀ながるの
ペンギン先生 ペンギン先生
userが返した倀が、その䞭のnameやpostsなどを解決するための芪の倀になるよ。Queryの同じ階局は䞊行に実行できるけれど、子は必芁な芪の倀が埗られおから進むんだ。
ひよこ ひよこ
曎新する順番も自由なの
ペンギン先生 ペンギン先生
Mutationの最䞊䜍のフィヌルドは、操䜜に曞かれた順で盎列に実行するよ。スキヌマ定矩の䞊び順ではないし、子のフィヌルドすべおが盎列になるわけでもない。DBのトランザクションも別に蚭蚈するよ。
ひよこ ひよこ
䞀぀の項目で゚ラヌになったら
ペンギン先生 ペンギン先生
実行䞭の゚ラヌはerrorsぞ蚘録され、該圓する倀はnullずしお扱うよ。ただしNon-Null!の項目なら芪ぞ䌝わり、data党䜓がnullになる堎合もある。構文や怜蚌で倱敗した堎合は実行前に止たるんだ。
ひよこ ひよこ
䞀芧の人数だけDBぞ問い合わせるの
ペンギン先生 ペンギン先生
玠朎な実装では、䞀芧1回ず各ナヌザヌの関連デヌタN回でN+1になるこずがある。DataLoaderでたずめる方法もあるけれど、DBぞたずめお問い合わせるバッチ関数は自分で甚意する必芁があるよ。
ひよこ ひよこ
認蚌情報はどう枡すの
ペンギン先生 ペンギン先生
倚くの実装にはcontextずいう共有倀があるよ。利甚者の暩限やDBアクセスを枡し、芋せおよい倀だけを返す。DataLoaderのキャッシュも通垞はリク゚ストごずに䜜り、別の利甚者ず混ぜないようにするんだ。

たず、ひよこの名前を取り出す

GraphQL入門ず同じく、Node.jsず新しい䜜業フォルダヌを䜿いたす。次の準備をしおから、コヌドを resolver-demo.mjs に保存しおください。

npm init -y
npm install graphql
import { buildSchema, graphql } from 'graphql';

const schema = buildSchema(`
  type User { name: String! }
  type Query { user(id: ID!): User }
`);
const users = { '1': { name: 'ひよこ' } };
const rootValue = {
  user({ id }) {
    console.log('探したID:', id);
    return users[id] ?? null;
  },
};
const result = await graphql({
  schema,
  source: '{ user(id: "1") { name } }',
  rootValue,
});
console.log(JSON.stringify(result));

node resolver-demo.mjs で次の結果になりたす。

探したID: 1
{"data":{"user":{"name":"ひよこ"}}}

user が返したオブゞェクトの name をGraphQL.jsの既定の凊理が読みたす。IDを "2" にすれば、この䟋では芋぀からず user が null になりたす。ここではHTTPサヌバヌやDBを䜿わず、解決凊理だけを詊しおいたす。

芪の倀から、必芁な項目をたどる

リゟルバヌは䞀般に芪の倀、匕数、context、実行情報を受け取りたす。本文の buildSchema ずルヌトオブゞェクトを䜿う方法では、ルヌトのメ゜ッドは匕数オブゞェクトを受け取りたす。すべおの曞き方で同じ関数の匕数順になるわけではありたせん。

user → posts → title なら、芪が投皿の䞀芧を返した埌、各投皿のタむトルを解決したす。「必芁な項目だけ返る」こずず、「裏偎の取埗も最小回数になる」こずは別です。DBや倖郚APIぞの呌び出し回数を確認したしょう。

゚ラヌず曎新の順序

通垞のフィヌルドは結果が順番に䟝存しないように蚭蚈したす。Mutationの最䞊䜍は操䜜の蚘茉順で盎列ですが、耇数の倉曎が䞀぀のトランザクションになる保蚌はありたせん。

nullableなフィヌルドの゚ラヌなら正垞な郚分のデヌタを返せる堎合がありたす。Non-Nullの倱敗は、nullを蚱す芪たで䌝わりたす。画面ではHTTPステヌタスだけでなく data ず errors を確認しおください。RESTでも郚分的な結果を返す蚭蚈は可胜で、単玔な「党郚成功か党郚倱敗か」の違いではありたせん。

もう少し詳しくN+1ずDataLoader

DataLoaderのJavaScript実装は、同じ実行のたずたりで芁求されたキヌをバッチ関数ぞ枡したす。その関数がキヌごずにDBを呌んでいたら、DBの問い合わせは枛りたせん。結果はキヌず同じ長さ・順番で返す必芁がありたす。

通垞はcontext内にリク゚スト専甚のDataLoaderを䜜りたす。別の利甚者でむンスタンスを䜿い回すず、暩限の違うデヌタをキャッシュから返すおそれがありたす。JOINや䞀括取埗も候補にし、実際の問い合わせ数を枬りたしょう。

ペンギン先生のたずめ

「リゟルバヌ」っお出おきたら「指定された項目の倀を甚意する担圓」ず思えばだいたいOK 次はGraphQLずRESTの比范で、API党䜓の蚭蚈も比べおみたしょう。

参考資料