Marginalia

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

GraphQL の Schema から静的なドキュメンテーションページを生成するツールとして、graphdoc というものがあります。

GraphQL Schema を渡すと、こんな感じの HTML を生成してくれます。 これは GraphQL 公式のサンプルにもある Star Wars API の例。

このドキュメンテーションを 複数の*.gqlファイルからなる Schema から生成するために少し小細工が必要だったので紹介します。

GraphQL Schema Language

今回は GraphQL Schema Language を.gqlという拡張子のファイル(便宜的に以降 gqlファイル と呼びます)で管理します。 TypeScript や JavaScript ファイル中でテンプレートリテラルとして管理するよりも余計なコードがなくて見やすく、エディタによっては入力支援も得られます。

gql ファイルの欠点は、GraphQL Schema Language に外部ファイルの参照の仕様がないことです。 JSON Schema でいう$refに相当するものが定義されていないので、エントリポイントとなる Schema から分割された型定義などを参照できません。

graphdoc で gql ファイルをドキュメンテーションする

graphdoc はいくつかの方法で Schema を読み込めます。 代表的なものはエンドポイント URL を渡して HTTP 経由でドキュメンテーションを生成する機能ですが、 gql ファイルを渡すこともできます。 しかし gql ファイルを渡す場合は先述の理由により単一ファイルにすべての Schema が記述されている必要があるので、 大きな API 仕様になると管理が大変です。

複数の gql ファイルからなる Schema を graphdoc で読み込むためには、JavaScript 読み込み機能を使います。 graphdoc のschemaFileとして.jsファイルを渡すと、CommonJS として読み込まれ、exports.defaultの値を Schema として使用します。 そしてこのとき、エクスポートするのは Schema の配列なので、複数の gql ファイルを読み込んで文字列として渡してあげれば目的を達成できます。

手順

npm パッケージを揃える

必要なのは@2fd/graphdocglobの 2 つです

> yarn add --dev @2fd/graphdoc glob

schema.jsファイルを記述する

複数の gql ファイルを読み込んで文字列として渡してあげるための JavaScript ファイルを用意します。 globfsを使ってファイルを読み込むだけですが、同期的にエクスポートする必要があるのに注意します。

const glob = require("glob");
const fs = require("fs");
const path = require("path");

function loadSchemas() {
  const files = glob.sync("schema/**/*.{gql,graphql}");

  return files.map(file =>
    fs.readFileSync(path.resolve(file), { encoding: "utf8" })
  );
}

const schemas = loadSchemas();
exports.default = schemas;

npm script を追加する

graphdocコマンドを実行する npm スクリプトを追加します

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

schemaディレクトリに gql ファイルを配置する

あとはひたすら Schema を書いていくだけです。

実際に Staw Wars API の Schema を分割して graphdoc にしたリポジトリを用意したので、詳細はそちらを参照してください。

docsディレクトリに出力しているので、GitHub Pages で簡単に API ドキュメントをホストできます。

課題

  • gql ファイルに外部参照の仕組みがないので、分割すると参照が途切れる(リネームとかできない)

サーバーサイドの実装に依存しない API 仕様 Centric な開発をしたいのだけど、 Open API Specification みたいな感じで GraphQL Schema を使う文化はあまり育っていないようで、苦労しそうな感じがあります。

結局のところひとつの gql ファイルにドカドカ型定義書いていくほうが楽なのかもしれないけど、しばらく分割管理を試してみてから考えましょう。