動作確認この章についてN+1問題に対してDataloaderを使い対処したように、複雑なクエリが来たとしてもDBにかける負担を減らす方法というのは確かに存在します。どんなにリゾルバ内の処理を工夫したとしても、そもそものリクエストクエリが複雑で、取得対象となるデータが膨大になっている場合にはやはりサーバーにかかる負荷は重くなってしまいます。そのため、「あまりにも複雑なクエリはそもそも受け付けないようにする」という機能をGraphQLサーバーにつけることがあります。今回はその「クエリ複雑度の制限」をサーバーにつける方法を紹介します。「あまりにも複雑なクエリはそもそも受け付けないようにする」という機能のためには、クエリの複雑度を定量的に決定する必要があります。ここではまず、デフォルトでのクエリ複雑度の計算法を解説したいと思います。具体例となるクエリ今回具体的に複雑度を計算するクエリとして、以下のものを使用します。}}このクエリを実行してみると、以下のような結果が返ってきます。}}複雑度の計算リクエストクエリに含まれる記述の中で、複雑度の計算にそのフィールドが絡むかどうかは「レスポンスとなるjsonに対応するキーが現れるかどうか」で決定されます。}}これらを全て合計すると12となります。このクエリの複雑度は12ということです。仕方をわかっていただけたところで、本命の「クエリ複雑度がある閾値以上だった場合はクエリ実行させない」という設定をサーバーに施してみましょう。Server構造体のUseメソッドGraphQLのサーバーにはUseメソッドという、サーバーにエクステンションを組み込むためのメソッドが用意されています。graphql.HandlerExtension)GraphQLサーバーのエクステンションエクステンションは「サーバーに便利な追加機能を組み込む」ためのものだと解釈してください。github.com/99designs/gqlgen/graphql/handler/extensionパッケージ内にエクステンションがいくつか用意されており、その中に「ある一定の閾値を超えた複雑度のクエリは実行させない」エクステンションも存在します。*ComplexityLimitエクステンションの導入クエリ複雑度を制限するためのエクステンションを、Useメソッドを用いて導入したコードがこちらです。今回は、実行可能なクエリの複雑度を最大10に設定したため、冒頭に紹介した複雑度12のクエリは実行できません。実際にクエリの実行を試みると、以下のようなエラーが得られることがわかります。null}複雑度計算法のカスタマイズデフォルトの複雑度計算の方法は、全てのフィールドに同じ重みづけを行っています。クエリフィールドごとにデータ取得処理の重さが異なるため、それを複雑度にも反映させたいという場合もあるかと思います。ここからは、複雑度計算の方法をカスタマイズする方法を紹介します。具体例のクエリ例えば、以下のようなクエリを考えてみます。}}issuesでの取得個数を幾つに変えたとしても、デフォルトの計算方法ではこのクエリの複雑度は7のままです。firstパラメータで指定するissueの取得個数が1つの場合と100個の場合で、処理の重さは異なるはずです。そのため、「Issueの取得個数が多ければ多いほど、複雑度を高くする」ように計算方法を変えたいです。複雑度計算をサーバーConfigで指定するサーバーで利用する複雑度計算ロジックは、サーバーエンドリポイントにて記述するConfigの内容で決定されます。nil))}internal.Config構造体の内容と、そのConmplexityフィールドの内容に何を指定すればいいのかは、gqlgenコマンドによって自動生成されたコード内にて定義が記述されています。int}}つまり、カスタムの複雑度計算を導入するということは、その独自の計算法を盛り込んだComplexityRoot構造体を作るということとイコールになります。独自の複雑度計算ロジックの実装それでは、サーバーConfigに渡すための独自のComplexityRoot構造体を作っていきましょう。ファイルの作成まずは、独自ComplexityRoot構造体変数を作るためのファイルを新しく作りましょう。go.sumComplexityRoot構造体の作成ファイルを作成したところでいよいよ実装に入っていきます。c}今回やりたいことは、「Repositoryオブジェクト内にあるissuesフィールドで取得するIssue数とクエリ複雑度を比例させたい」ということなので、ComplexityRoot構造体の中のRepository.Issuesフィールドに設定を記述しています。Repository.Issuesフィールドに記述する複雑度決定ロジックRepository.Issuesフィールドには、以下のような関数を指定することができます。int}}引数の中でafter・before・first・lastは、リクエストクエリで指定された入力パラメータの値が格納されています。戻り値のintに独自に計算したクエリ複雑度を指定して返すのがこの関数内で行う処理です。childComplexityを採用することで「Issueの取得個数が多ければ多いほど、複雑度を高くする」要件を作っています。childComplexity}第一引数childComplexityの内容ここで登場するのが第一引数のchildComplexityです。この引数には「取得されるIssueのクエリ複雑度」が格納されています。issuesフィールド以下の複雑度合計は4なので、childComplexityの値は4になります。自作の複雑度計算ロジックを実装できたところで、それをサーバーに反映させましょう。nil))}動作確認それでは、デフォルトの複雑度計算ロジックでは複雑度7だった先ほどのクエリが、今回カスタムした内容ではどのくらいになるのか確認してみましょう。サーバーを起動してクエリを実行してみた結果がこちらです。null}firstの値を変化させると、その増減に応じて複雑度の計算結果も変わることが確認できるかと思います。