Help us understand the problem. What is going on with this article?

スキーマからコード生成してハッピーになる

GraphQL Advent Calendarの16日目です。
この記事ではGraphQLのスキーマから型ファイルを生成するツールであるgraphql-code-generatorを紹介します。
似たようなツールであるgraphqlgenとの違いも簡単に説明します。

TL;DR

  • GraphQLスキーマとは別にスキーマに対応したモデルやリゾルバのシグネチャなどのファイルを用意するのは冗長な作業でなんとかしたい
  • graphql-code-generatorを使えばGraphQLスキーマに対応したTypescriptなどの言語の定義ファイルを生成してくれる
  • プラグインが豊富でサーバーサイドだけでなくreact-apolloなどに対応したコードの生成も可能
  • prismaのgraphqlgenもあるけど機能的にはgraphql-code-generatorのほうが充実している
  • リゾルバーだけならgraphqgenで十分

背景

GraphQLにはGraphQL SDL (Schema Definition Language)とよばれるAPIを定義するための独自の言語があります。
もちろんSDLを書くだけでは不十分で「このフィールドに対してはこの処理をしてこの値を返す」というような
リゾルバーを実装する必要があります。実装に際してスキーマの型に対応したTypescriptの型を定義したり、
リゾルバーのシグネチャを定義するのですが、スキーマとTypescriptでの型定義とで大分重複するところがあり (というかほぼ同じ)、なんか冗長な作業だなと思っていました。
なんかいいツール無いかなと調べて出会ったのがgraphql-code-generatorです。
今すぐ試してみたい方はライブデモをどうぞ :smile:

graphql-code-generatorとは

GraphQLスキーマからTypescriptの定義ファイルを生成してくれるツールです。1
Golangでは似たツールとしてgqlgenがあります。
このツールには以下の特徴があります。

  • スキーマファースト
    スキーマを元にコードを生成するので、実装言語での冗長な作業が防げる。
  • 型安全
    リゾルバーの引数なども全て型を与えるので、打ち間違えなどで存在しないフィールドにアクセスしてしまうみたいな問題を防げる

graphql-code-generatorの仕組み

graphql-code-generatorはデフォルトでcodegen.ymlcodegen.json2という設定ファイルを読みに行き
どのパイプラインで各ファイルを生成するかを決めます。
例えば以下のcodegen.ymlでは、スキーマ./schema.graphqlから./server.ts./resolver.tsを生成する設定をしています。
各ファイルの生成パイプラインはplugins以下に列挙して指定します。
公式にも書かれているとおり、プラグインごとにスキーマが独立して処理され、各プラグインの出力が単純に上から順番にconcatenateされるだけです。
これは何を意味するかというと、各ファイルでプラグインを共有していたとしても(以下の例だとtypescript-common)、重複するコードが各ファイルに出力されてしまうということです。
後ほど説明するaddというプラグインを使って共通なコードをimportするコードをインジェクトするのが解決策の一つかなと思います。

codegen.yml
schema: ./schema.graphql
generates:
  ./server.ts:
    plugins:
      - add: "/* コメントなど任意のコードを追加できます。 */"
      - time
      - typescript-common
      - typescript-server
  ./resolver.ts:
    plugins:
      - typescript-common
      - typescript-resolvers

上の./codegen.ymlで使われているプラグインの説明を簡単にしておきます。

  • add
    任意の文字列を出力する。 tslintをコメントで設定したりだとか、サーバー側とクライアント側で共有したいコードをimportしたりしたいときに使えそうです。
  • time
    出力時の時間を出力する。
  • typescript-common 列挙体、スカラー、入力型のようなサーバー側とクライアント側で共通して使うようなものを出力する。
  • typescript-resolvers
    サーバーサイドのリゾルバーのシグネチャの出力する。
  • typescript-server
    サーバーが使うコード (types, directives, interfaces, unions) を出力する。

他にもqueryやmutationなどのドキュメンのからクライアント側が使うコードを出力するプラグインや、
react-apolloのコンポーネントを出力するものなどの色々あります。
詳しくは公式ページをご覧になってください。

実際に使ってみる

まずはpeerDependencyであるgraphqlgraphql-code-generatorを入れましょう。
次に必要なプラグインもインストールしちゃいます。ここでは上で紹介したプラグインのみを入れます。
(以下ではyarnを使っていますが、npmを使ってもいいと思います。)

yarn add -D graphql graphql-code-generator
yarn add -D graphql-codegen-add graphql-codegen-time graphql-codegen-typescript-common graphql-codegen-typescript-resolvers graphql-codegen-typescript-server

次にcodegen.ymlをプロジェクトのルートに記述します。今回は上述のものを使います。

次にスキーマファイルを用意します。

schema.graphql
schema {
  query: Query
}

type Query {
  user(id: ID!): User
}

enum Role {
  USER
  ADMIN
}

type User {
  id: ID!
  username: String!
  email: String!
  role: Role!
}

あとはgraphql-code-generatorのスクリプトであるgql-genを実行するだけなのですが、
グローバルにインストールしていないのでpackage.jsonにスクリプトを追加します。

package.json
 ...
 "scripts": {
    ...
    "gen": "gql-gen",
    ...
 },
 ...

これでコードを生成する準備ができましたので生成してみましょう!

yarn gen

プロジェクトルートにserver.tsresolver.tsができていると思います。
中身の詳細は説明しませんが、主に以下の内容が出力されています。

  • Roleに対応する列挙体
  • リゾルバーのシグネチャ
  • オブジェクトの型(QueryUser)
  • user(id: ID!): Userの引数の型

あとはこのファイルを別ファイルでインポートして使うだけです。

graphqlgenとの違い

似たようなツールとしてPrismaのgraphqlgenがあります。
graphqlgenのGitHubのIssueにGraphQL Code Generatorの著者の方が違いはどこにあるのかという質問をしています。
graphqlgenの著者の回答を簡単にまとめると、
graphqlgenはJavascriptベースの型言語で、サーバーサイドのGraphQLリゾルバーのコード生成に特化しているのに対して、graphql-code-generatorはより一般的なツールであるということです。
具体的には、

  • フロントエンドとバックエンドのスコープ
    • graphqlgenはサーバーサイドのリゾルバー生成に特化しているので、モデルなどは自分で定義する必要がある
    • graphql-code-generatorはreact-apolloなどフロントエンドも網羅している
  • サポートするプログラミング言語 graphqlgenはTypescript, FlowなどのJavascriptのエコシステムに特化している。 これに対して、graphql-code-generatorの作者はgraphql-code-generatorも元々Typescriptファーストで作られており、サポートをする言語の汎化を行っているとは一切言っていないと反論しています。 確かにドキュメントを見た感じでは生成されたファイルはすべてがTypescriptとなっているのでこの主張はよくわかりません。 ただ、graphql-code-generatorはプラグインを自前で用意することで基本的にTypescript以外の言語に出力することは可能です。
  • APIの設計や設定
    graphql-code-generatorの設計はgraphqlgenの開発者が満足できるものではなかったようです。

使ってみた感想

  • graphqlgenはリゾルバに特化しているのでシンプルな一方で、スキーマ以外にも用意しないといけないファイルがあるのでちょっと面倒です。それに対して、graphql-code-generatorはスキーマを食わせるだけで、リゾルバーのインターフェスはもちろんモデルも生成してくるのでかなり便利だと思います。
  • graphql-code-generatorはファイルごとに変換処理が独立しているので、共通するコードを切り分けてimportしてくれません。 そうすると、typescript-commonの出力結果である列挙型の定義がどちらのファイルにも現れてしまいます。 ファイルごとに使うプラグインを定義するので難しいかもしれませんが、もう少し上手い方法があってもいいと思います。 それに対してgraphqlgenはファイルの分割戦略を設定で変更することができ、importもしてファイルを関連付けてくれます。

まとめ

今回はGraphQLのスキーマからTypescriptなどのコードを生成するツールであるgraphql-code-generatorとgraphqlgenを紹介しました。
graphql-code-generatorのほうが機能は充実しているのですが、graphqlgenのいいところ(ファイルの分割戦略)もあります。
GitHubのissueを見ると対立的な雰囲気があるので、お互いが協力しながら一つのよいOSSを作り上げていってほしいと思っています。


  1. プラグインを作成可能なので他の言語も対応可能です。 

  2. 設定で変更可能です。 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away