【NestJS】GraphQLサーバーの起動からエラーハンドリングをするまでをソースコードで追いかける
動機
NestJSでGraphQLサーバーを実装するにあたって、エラーハンドリングをGraphQLの仕様にしたがって適切に行いたかったが(extensionsにエラーを追加したかった)、そもそもNestJS上でのリゾルバのエラーがどのようにGraphQLのエラーとしてthrowされるのか知らなかった為。
前提
GraphQLサーバーの実装には@nestjs/graphqlのGraphQLModuleを使用します。HTTPプロバイダーとしてはデフォルトのexpressを使用する前提でコードを読んでいきますが、おおよその流れとしてはfastifyを使用する場合と同様です。
GraphQLサーバーの起動まで
GraphQLModule
NestJSでモジュールをimportすると、onModuleInitというメソッドが自動で実行されます。184行目で_graphQlAdapterクラスのstartメソッドが実行されています。
ApolloDriver
この_graphQlAdapterクラスの正体はApolloDriverで、startメソッドを実行すると、36行目でregisterServerが実行されます。
registerServer
registerServerでは、HTTPプロバイダとしてexpressを使っているか、fastifyを使っているかを判別して、処理を分けています。expressを使用してるので、58行目のregisterExpressが実行されます。
registerExpress
expressでapolloを使用する場合、expressMiddlewareというメソッドを使用して、初期化したapolloサーバーをミドルウェア化する必要があるので、registerExpressの中ではその処理を行っています。
Apolloサーバーによるリゾルバのエラーのハンドリング
expressMiddleware
長いの抜粋ですが、ここでGraphQLのクエリを含んだHTTPリクエストをハンドリングしているようです。109行目でexecuteHTTPGraphQLRequestのエラーをキャッチして次のミドルウェアに渡しています。
executeHTTPGraphQLRequest
executeHTTPGraphQLRequestはrunPotentiallyBatchedHttpQueryというメソッドの結果を返しています。ここからは実際のクエリの解決になり複雑になってくるので追いかけませんが、このメソッドの中で、GraphQLエラーがthrowされるようです。
結局、エラーにextensionsを追加するにはどうすればいいのか
エラーの正規化のメソッドのコードを読むと、基本的にGraphQLエラーはそのままthrowできるようになっているみたいなので、下記のようにgraphqlパッケージのGraphQLエラーをそのまま使うのが良さそうかなと思いました。
throw new GraphQLError('user aleady exists', {
extensions: {
userErrorMessage: '該当のユーザーは既に存在します。',
},
});
Discussion