余白

https://blog.lacolaco.net/ に移転しました

merge-graphql-schemasを使ってGraphQLのスキーマファイルを結合する

lacolaco.hatenablog.com

^の続きです。 前回はglobとfsを使ってschema/**/*.gqlファイルを結合し、graphdocを使ってドキュメンテーションページを生成する記事を投稿しました。

まったく同じ目的のために merge-graphql-schemas というnpmパッケージがあったので、これを使ってドキュメンテーションのビルドスクリプトを改良しました。

パッケージのインストール

前回の状態から、globをアンインストールして、代わりにmerge-graphql-schemasをインストールします。

$ yarn remove glob
$ yarn add --dev merge-graphql-schemas

schema.jsの変更

前回はschema.jsからエクスポートした文字列配列をスキーマとしてgraphdocに読み込ませましたが、 今回はschema.jsを実行することで静的なschema.gqlファイルを書き出し、これをgraphdocに渡します。

結合されたスキーマが静的ファイルとして存在することで、他のGraphQL周辺ツール(コード生成など)への連携が容易になります。

schema.jsは次のように書きました。fileLoadermergeTypesmerge-graphql-schemasパッケージから提供される関数です。

最終的に結合された文字列がschema.gqlファイルに書き出されます。

やっていることは前回とだいたい同じですが、この関数でマージされたスキーマは内部でスキーマ単位にソートされていて、ファイル名ではなくtypeinterfaceの名前でソートされているようです。

const path = require('path');
const fs = require('fs');
const { fileLoader, mergeTypes } = require('merge-graphql-schemas');

const typesArray = fileLoader('schema/**/*.{gql,graphql}', {
  recursive: true
});

const mergedSchema = mergeTypes(typesArray);

fs.writeFileSync('schema.gql', mergedSchema, {encoding: 'utf8'});

こんな感じで、schemaから順番に何らかのルールでソートされています。

schema {
  query: Query
  mutation: Mutation
}

type Query {
  # Return the hero by episode.
  hero(episode: Episode): Character
  # Return the Human by ID.
  human(id: ID!): Human
  # Return the Droid by ID.
  droid(id: ID!): Droid
}

type Mutation {
  # Save the favorite episode.
  favorite(episode: Episode!): Episode
}

# A character in the Star Wars Trilogy
interface Character {
  id: ID!
  name: String
  friends: [Character]
  appearsIn: [Episode]
  secretBackstory: String
}
...

graphdocでスキーマを読み込む

package.jsonbuildスクリプトを次のように変更します。事前にschema.jsを実行し、吐き出されたファイルをgraphdocに渡します。

  "scripts": {
    "build": "node schema.js && graphdoc -s schema.gql -o docs -f"
  },

これで元どおりドキュメンテーションが生成されます。


schema.gqlを静的に吐き出してGit管理に含めておくと、このリポジトリを外部から参照することで簡単にGraphQLのスキーマが得られるので、 クライアントサイドとサーバーサイドそれぞれから参照して利用するのに便利です。.gqlファイルなので実装の言語も問いません。

この方法で作成したスキーマからTypeScriptの型定義を生成してクライアントサイドで利用するのを仕事で試しているので、いい感じに知見が溜まったらまた記事を書きます。

だんだんAPI仕様中心開発っぽくなってきたぞ!さらばバックエンド実装にブロックされるフロントエンド開発!