GraphQLクライアントがリクエストするURLにoperationNameを付けると便利です
GraphQLのクエリやミューテーションはJSONエンコードされた本文をHTTP POSTでサーバに送ることで実行されます。
すべてのクエリやミューテーションは同じエンドポイント(/graphql
等)に対してPOSTされるため、特別な対応をしない限り、ログには「クエリ or ミューテーションどちらなのか」「どんな内容か」などの情報が記録されません。
簡単な設定だけでログを見てざっくりリクエストの傾向がわかるようにできると便利ですよね。
エンドポイントにoperationNameを付与する
例: query FooBarQury
を実行した場合 -> /graphql?operationName=FooBarQuery
上記のようにしてクエリやミューテーションの名前を operationName
としてリクエストURLに付与します。
具体的な情報はもちろん本文に含まれるため、細かな分析はできませんが、ログに記載された operatioName
が分かれば、コードベースを grep するだけでどんなクエリを送っているのか調べることができます。便利ですね! [1]
設定方法
GraphQLクライアントでは初期化時の設定やリクエスト送信時のオプションで fetch
をカスタマイズすることが可能ですので、各ライブラリに合わせて設定を行います。
urqlの場合
いきなり fetch
をカスタマイズする必要はありません。 urql は exchange という仕組みで、通信やキャッシュの処理に介入することができます。
あまり詳しくはないのですが、自前で exchange を書かなくても @urql/exchange-context
でクエリ実行前に context に介入することができるようです。 [2]
getOperationName
というユーティリティー関数を使用して DocumentNode
から名前を取得します。( urql 4.x からは使用できなくなっていますので、コアを参考に自前で実装してください)
import {
createClient,
dedupExchange,
cacheExchange,
fetchExchange,
getOperationName, // urql 3.x
} from "urql";
import { contextExchange } from "@urql/exchange-context";
// urql 4.x
// https://github.com/urql-graphql/urql/blob/af5b90b7bb9aec77638a44671e9cddff531c16e1/packages/core/src/utils/request.ts#L170C1-L176C3
const getOperationName = (query: DocumentNode): string | undefined => {
for (const node of query.definitions) {
if (node.kind === Kind.OPERATION_DEFINITION) {
return node.name ? node.name.value : undefined;
}
}
};
/** リクエストのURLにoperationNameを付与する */
const annotatedURL = (operation: Operation) => {
// contextは使い回されるため、パラメーターが既に存在する場合を考慮してURLクラスを使用してます
// 相対パスを処理する良い方法がわからなかったためダミーのbase URLを指定しています。
const url = new URL(operation.context.url, "http://example.com");
url.searchParams.set(
"operationName",
getOperationName(operation.query) ?? ""
);
return url.host === "example.com"
? `${url.pathname}${url.search}`
: url.toString();
};
const client = createClient({
url: "/graphql",
exchanges: [
// この辺はデフォルトに含まれているexchange
dedupExchange,
cacheExchange,
contextExchange({
getContext: async (operation) => ({
...operation.context,
url: annotatedURL(operation),
}),
}),
// fetchより前に入れる
fetchExchange,
],
});
ApolloClientの場合
実装例がドキュメントにあります。
HttpLink
を作成して fetch
に介入することでURLを変更します。
import { ApolloClient, from, HttpLink } from '@apollo/client'
const customFetch = (uri, options) => {
const { operationName } = JSON.parse(options.body);
return fetch(`${uri}?opname=${operationName}`, options);
};
const link = new HttpLink({ fetch: customFetch });
const client = new ApolloClient({
link: from([link])
})
まとめ
GraphQLクライアントの設定やプラグイン機構を利用することでHTTP通信をカスタマイズできます。
自分のプロジェクトではこの仕組みを使い、APIトークンを付与したり、ローカル環境で臨場感が出るようにディレイを入れたりしています。
-
urqlでも下記のApolloClientのようにfetchをカスタマイズすることができるので、その方法でもよいのですが、そのままだと https://github.com/jaydenseric/graphql-multipart-request-spec を使用したい場合に対応できていないと思うのでcontext部分に介入する方法を選択しました。 ↩︎
Discussion