最近は GraphQL やっていきな感じです。ただスキーマ設計が色々と悩ましく、設計の参考にするためにGraphQL Schema Design @ Scale (Marc-André Giroux) を観たのでかなり雑にメモ。
※ 英語のプレゼン動画をさくっと観ただけなので、正しく理解できていない所もあるかもしれません。
なぜ GraphQL を使うのか
GraphQL スキーマデザインについての誤解
- 複雑なアプリケーションにおいて、GraphQL はそのまま DB のインターフェースになることはめったにない
- GraphQL スキーマは既存の REST API のリソースにマッチする必要はない
- UI と 1:1 でマッピングされる必要はない
GraphQL の真の力
良い GraphQL スキーマをつくるための 2 つのポイント
- アプリケーションのドメインのエキスパートになり、知識を身につける
- GraphQL 特有の設計のエキスパートになる
そしてこれらを同じ人間が担当することはめったにない。
GitHub のアプローチは
Guiding Principles
データよりも振る舞いやユースケースを重視した設計
type PullRequest { title: String! description: String! ciStatus: CIStatus! reviews: [Review!]! }
クライアントがこの PR がマージ OK かどうか知りたい場合、以下のようなチェックが必要になるはず。
fun mergeable?(pr) pr.ciStatus == CI_STATUSES.GREEN && pr.reviews.all? { |r| r.approved }
でもクライアントはただマージできるのかどうか知りたいのでスキーマ的には以下のような形で十分。
type PullRequest { title: String! description: String! ciStatus: CIStatus! reviews: [Review!]! isMergeable: Boolean! }
マージ OK の条件が今後変わっても、それはサーバ側で吸収することでクライアントはただマージできるかどうか見ていればよい。
スマートで汎用的なフィールドよりも高度に最適化されたフィールド
type Query { user(id: ID, login: String): User }
nullable なid
かlogin
を受け取れるような設計より、それぞれのアプローチに最適化されたクエリを定義して型の恩恵を受けたほうがよい。
type Query { userById(id: ID!): User userByLogin(login: String!): User }
Tips
SDL のチェック
$ bin/dump-graphql-schema > config/schema.public.graphql $ git add config/schema.public.graphql
みんなスキーマをどんどん更新していくが、GitHub はgraphql-ruby
を使っているのでスキーマにどのような変更があったのかぱっと見てわかりにくい。上記のようなアプローチによって
スキーマ設計ミスった時や breaking changes を入れたい場合
@deprecated
を使うoldField (@deprecatedつき)
,newField
をメンテする- GitHub ではすべての@deprecated をあつめて breaking changes ページに公開してメンテしている
deprecated( start_date: ..., reason: ..., superseded_by: .... ) type Repository { databaseId: ID! @deprecated(reason: ...) }