🎨

【GraphQL】カスタムスカラーの使用パターン集

2023/10/06に公開

カスタムスカラーとは?

GraphQLでは、IntFloatStringBooleanIDの5つのデフォルトスカラーが提供されており、これらを用いてデータを表現できます。また、ユーザーはカスタムスカラーとしてスカラーを独自に定義して利用することも可能です。例えば、URIをカスタムスカラーとして定義してみます。

schema.graphqls
scalar URI

type File {
  title: String!
  url: URI!
}

GraphQLのデフォルトとして定義されているスカラーは5つしか用意されておらず、したがって、データベースで一般的に利用されるDateTime型やJSON型を使用したい場合、それらをカスタムスカラーとして自分で定義する必要があります。

さらに、極端な話、全てのフィールドの型をカスタムスカラーとして再定義することも可能です。

schema.graphqls
scalar UserName
scalar UserAge

type User {
  name: UserName!
  age: UserAge!
}

このようにカスタムスカラーとしてGraphQLユーザが新しい型を自由に定義できるため、プロジェクトのドメインに最適なスキーマを作成することが可能です。また、型付けによりフィールドのフォーマットが分かるため、GraphQLのクライアントにバリデーションの必要性を明示することが可能です。

しかし、この自由度の高さが故に、不必要なカスタムスカラーの定義や不適切な型の割り当てなど、実装上注意すべき事項が増える傾向があります。

この記事では、私の所属するハコベル配車計画チームが1年間にわたりGraphQLを運用して知見として得たカスタムスカラーの定義パターンをまとめます。GraphQLの設計に悩む他の開発者の方々にとって、参考になる情報となれば幸いです。

カスタムスカラーの使用パターン

パターン1. 普遍な要素のみをカスタムスカラーとして定義

普遍な要素とは、特定のドメインに依存せず、特定のフォーマットを持つ要素を指します。具体的な判断基準として、以下のような要素が挙げられます。

  • PreciseDateTime(ISO 8601)
  • URI(RFC 3986)
  • JSON(RFC 8259)

普遍な要素を判断する際の良い基準として、それらが標準化機関で定義されているかどうかを考えることができます。

また、GraphQLの仕様ではフォーマットを明示するために@specifiedByディレクティブの使用が推奨されています。

schema.graphqls
scalar URI @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")

メリット

  • 判断基準を満たす要素は一般に少ないため、スカラーの定義に悩む時間を減らすことが可能
  • カスタムスカラーを普遍な要素のみに限定できるため、カスタムスカラー定義によるスキーマの肥大化を抑えることが可能

デメリット

  • 普遍な要素に該当するものは少なくその他の要素を定義できないため、スキーマの表現力が減少する

パターン2. ビジネスルールを表現したい要素をカスタムスカラーとして定義

ビジネスルールとは、アプリケーション固有のルールのことを指します。DDD(ドメイン駆動設計)の文脈では、これはドメイン知識として扱われます。

たとえば、ECサイトにおいてポイント総数に上限が存在する場合、そのルールがビジネスルールに該当します。この場合、pointフィールドをIntではなく、カスタムスカラーとしてPointとして定義することができます。

schema.graphqls
scalar Point @specifiedBy(url: "仕様を定義したドキュメントのリンク")

type User {
  point: Point!
}

なお、パターン2においてもカスタムスカラーを定義する以上、フォーマットを明示するために@specifiedByディレクティブの使用を推奨します。

メリット

  • GraphQLのクライアントにドメイン固有のバリデーションの実装を強いることが可能

デメリット

  • クライアントもビジネスルールを理解していることが前提になるため、開発組織全体でビジネスルールの認識を統一する必要があり、開発コストが上昇する
  • ビジネスルールの認識に齟齬がある場合、クライアントとサーバーサイドでバリデーション実装がずれうるため注意が必要

まとめ

「普遍な要素のみをカスタムスカラーとして定義」・「ビジネスルールを表現したい要素をカスタムスカラーとして定義」の2パターンを紹介しました。

上記メリット・デメリットを踏まえてハコベル配車計画チームでは、「普遍な要素のみをカスタムスカラーとして定義」する方針を採用しました。この方針の背後には、我々がDDD(ドメイン駆動設計)を開発スタイルとして採用していることが関連しています。そして、後者のデメリットの一つと関連して、ドメイン知識の実装がクライアントに漏れてしまい、ドメインモデル貧血症を引き起こす恐れがあるという懸念が存在しました。

GraphQLは非常に高い自由度を持っています。一方で、この高い自由度から不要な定義に悩まされ、スキーマの議論に多くの時間を費やすことがよくあります。このような経験をお持ちの方々にとって、本記事が参考になれば幸いです。


本記事の他に、同じチームのおおいし (@bicstone) さんより、「【GraphQL】スキーマ駆動開発におけるエラーレスポンス設計パターン集」や「【GraphQL】スキーマ駆動開発におけるバリデーションの取り決め設計パターン集」といった関連記事も執筆されています!!

https://zenn.dev/hacobell_dev/articles/graphql-error-response
https://zenn.dev/hacobell_dev/articles/graphql-validation

関連文献

  • Shopify GraphQL Design Tutorial

    Shopify GraphQL Design Tutorialでは、カスタムスカラーの定義ルールをフォーマットの明確さとGraphQLクライアント側の検証の複雑さという2つの軸で示しています。

    ルール #19: フォーマットが明確であり、GraphQLクライアント側の検証が複雑な場合には、入力に対して弱い型付け(EmailではなくString)を行うこと。これによりサーバーは一度にすべての検証を実行し、単一のフォーマットでエラーを報告することになり、結果としてGraphQLクライアントが非常にシンプルになる。
    (中略)
    ルール #20: フォーマットが曖昧でGraphQLクライアント側検証がシンプルな場合は強い型付け(DateTime)を行うこと。型付けにより明確さを得られ、GraphQLクライアントにより厳しい入力値管理(フリーテキスト入力ではなくカレンダーウィジェットを用いる)を促せる。

    Shopify GraphQL Design Tutorial#入力:-スカラー

    本記事のパターン1である「普遍な要素のみをカスタムスカラーとして定義」をさらに厳密に定義したものと言えます。

  • GitHub GraphQL API

    GitHub GraphQL APIでは、本記事のパターン1である「普遍な要素のみをカスタムスカラーとして定義」に加えて、一部の要素(例: GitObjectIDやGitRefnameなど)が、Git固有の知識であるものの、クライアントにとっては既知でフォーマットが指定されている場合、これらもカスタムスカラータイプとして定義されています。

    本記事のパターン1である「普遍な要素のみをカスタムスカラーとして定義」に加え、一部の場合にはパターン2である「ビジネスルールを表現したい要素をカスタムスカラーとして定義」も許容したものと言えます。

Hacobell Developers Blog

Discussion