Apollo Plugin
プラグインでやりたいこと
ロギング
パフォーマンス情報
リクエスト数
エラー数
認証
エラーハンドリング
GraphQLExtension API to the newer ApolloServerPlugin API.
graphql-extensions
実験的な API
トレーシングやパフォーマンスメトリクスを Apollo Engine に送信するための実装が前身
現在は Apollo Sever に統合されている
野良ライブラリもある
https://github.com/localshred/apollo-newrelic-extension\
一般向けには推奨されないが、代替が長らくなかった
既存の拡張
apollo-cache-control
apollo-tracing
https://github.com/apollographql/apollo-server/blob/master/packages/apollo-tracing/src/index.ts
plugins
2.2 から新たに導入された概念
数々の life-cycle hooks を持つ
hooks のスコープは限定的で、ニーズを慎重に検討して、拡張性/柔軟性の基礎にする段階
Apollo Server 3.0 に向けて
Plugin API
Aollo 2.2 から登場した
Plugin の使い所
Logging
メトリクスの通知
認証
エラーハンドリング
...
既存の Plugin API でも可能
さらにいくつかの hook を追加する予定
https://github.com/apollographql/apollo-server/pull/1795
https://github.com/apollographql/apollo-server/issues/2360
Events
Server lifecycle events
特定のリクエストに紐づかない、サーバ全体のイベント
Request lifecycle events
特定のリクエストに紐づくイベント
前者に後者がネストされている
Definition
apollo-server-plugin-base モジュールが ApolloServerPlugin インタフェースを expose している。
Plugin は、イベントをマッピングするオブジェクトを利用することでライフサイクルイベントを定義する。
イベントを、それぞ実装した関数にマッピングする。
めっちゃシンプルなプラグインは以下。requestDidStart を実装している。
code:js
{
requestDidStart() {
console.log('The request started.');
},
}
任意のオプションを引数にとり、ApolloServerPlugin インタフェースを実装したオブジェクトを返すような関数を提供できる。
code:javascript
/* localPluginModule.js */
module.exports = (options) => {
/* ...Plugin specific implementation... */
return {
requestDidStart() {
console.log('The options were', options);
},
};
};
これを利用する場合。
code:typescript
plugins: [
require('./localPluginModule')({
/* ...ここにオプションを渡す */
}),
],
Server lifecycle events
serverWillStart GraphQL サーバーの起動の準備時。非同期関数がマップされていたら、それが終了するまでサーバーは起動されない。他に依存する何かがある場合にサーバの起動を遅延できる
requestDidStart 必要であれば、Request lifecycle event の実装を返す。サーバがリクエストの処理を開始した際。GraphQLRequestListener を実装したオブジェクトを返す。これで特定の Request lifecycle events を実装する。
Request lifecycle events
https://github.com/apollographql/apollo-server/blob/ceee5db460f002a80b837ff0dba1743d4237aaf0/packages/apollo-server-plugin-base/src/index.ts
parsingDidStart requestContext を引数として受け取る。クエリドキュメントの構文木の解析前の段階
code:typescript
parsingDidStart?(
requestContext: GraphQLRequestContext<TContext>,
): (err?: Error) => void | void;
validationDidStart パースは終了している。document AST はこの時点で存在する
code:typescript
validationDidStart?(
requestContext: WithRequired<GraphQLRequestContext<TContext>, 'document'>,
): (err?: ReadonlyArray<Error>) => void | void;
didResolveOperation 実行される Operation が getOperationAST によって解析され正常に取得された後に実行される。これはドキュメントが複数存在する場合に重要。この時点だと、operationName (文字列) と operation (AST) がコンテキストに含まれる
code:typescript
didResolveOperation?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'document' | 'operationName' | 'operation'
,
): ValueOrPromise<void>;
executionDidStart
code:typescript
executionDidStart?(
requestContext: WithRequired<
GraphQLRequestContext<TContext>,
'document' | 'operationName' | 'operation'
,
): (err?: Error) => void | void;
`
willSendResponse
code:typescript
willSendResponse?(
requestContext: WithRequired<GraphQLRequestContext<TContext>, 'response'>,
): ValueOrPromise<void>;
context の移り変わり
リクエスト開始
クエリ文字列
パース開始
クエリハッシュ値の計算終了
検証開始
クエリドキュメントの解析完了して、クエリドキュメントオブジェクトになっている
解析終了
クエリドキュメントオブジェクトから operaiton オブジェクトが生成される
実行開始
レスポンスを返す
レスポンスデータが取れるようになる
ドキュメントとオペレーションの違い
オペレーションは
オペレーション種別がわかる (query か mutation か)
変数定義
ディレクティブ定義
GraphQL は仕様として、複数の operation を含めることができる。その場合には operation name を指定する必要がある。
https://stackoverflow.com/questions/49714484/why-would-you-send-multiple-operations-in-a-graphql-query-document-when-one-can
{"query":"# Write your query or mutation here\nquery A {\n itemByType(type: \"fuga\") {\n id\n users {\n id\n }\n }\n}\nquery B {\n itemByType(type: \"fuga\") {\n id\n users {\n id\n }\n }\n}", "operationName":"A"}'
https://graphql.github.io/graphql-spec/June2018/#sec-Executing-Requests