Marginalia

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

GraphQL Schemaをファイル分割してドキュメンテーションする

^の続きです。 前回は 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 仕様中心開発っぽくなってきたぞ!さらばバックエンド実装にブロックされるフロントエンド開発!