GraphQL-MarkdownでGraphQLスキーマからドキュメントを自動生成する

### はじめに

OpenAPIを[redoc](https://github.com/Redocly/redoc)等でドキュメント化するように、GraphQLのスキーマも見やすい形でドキュメント化して開発メンバーに共有することで、開発効率をあげることができます。
GraphQLスキーマからドキュメントを生成するライブラリはいくつかありますが、いくつか試したところ[GraphQL-Markdown](https://graphql-markdown.dev/)がいい感じだったので紹介します。

### 実行環境

```shell
$ node -v
v22.2.0

$ npm -v
10.8.0
```

### GraphQL-Markdownの良いところ

- ドキュメントが充実している
- specifiedByディレクティブが反映される
- カスタムディレクティブを反映できる
- メンテされている（2024／5現在）

### やってみる

まずは適当な`schema.graphql`を作成します。

```ts 
scalar Date @specifiedBy(url: "https://scalars.graphql.org/andimarek/local-date")

interface Book {
  "id"
  id: ID!

  "タイトル"
  title: String!

  "著者"
  author: String!

  "発売日"
  publishedDate: Date!

  "出版社"
  publishedBy: Company!
}

type Comic implements Book {
  "id"
  id: ID!

  "タイトル"
  title: String!

  "著者"
  author: String!

  "発売日"
  publishedDate: Date!

  "出版社"
  publishedBy: Company!

  "巻数"
  vol: Int!

  "ページ数"
  pages: Int!
}

type Magazine implements Book {
  "id"
  id: ID!

  "タイトル"
  title: String!

  "著者"
  author: String!

  "発売日"
  publishedDate: Date!

  "出版社"
  publishedBy: Company!

  "発行ナンバー"
  issueNumber: Int!

  "何月号か"
  month: Int!
}


enum Company {
  ABC_BOOKS
  KYOTO_SHUPPAN
  SHUEISHA
}

type Query {
  "全ての本のリストを取得"
  books: [Book]!
}
```

これをドキュメント化します。
基本的には[リファレンス](https://graphql-markdown.dev/docs/get-started)に従っていけばOKです。

GraphQL-Markdownは[Docusaurus](https://docusaurus.io/)ベースで作られています。DocusaurusはMeta社が作成したドキュメントの自動生成に特化した静的サイトジェネレーターだそうです。

- まずはテンプレートが用意されているので、それを使ってDocusaurusのセットアップをします

```shell
$ npm init docusaurus graphql-docs-example https://github.com/graphql-markdown/template.git
```

自動でファイルが生成されるので、これを修正していきます。

- 次にローカルのスキーマファイルに基づいてドキュメントを生成します。リファレンスによると、スキーマの読み込みはデフォルトでURL経由になっており、ファイルを読み取るには`@graphql-tools/graphql-file-loader`が必要らしいのでインストールします

```shell
$ cd graphql-docs-example
$ npm install @graphql-tools/graphql-file-loader
```
    
https://graphql-markdown.dev/docs/advanced/schema-loading
    

- 設定は`.graphqlrc`に記述します。schemaファイルのファイルパスとLoaderを修正します。

```diff: .graphqlrc
- schema: 'https://api.react-finland.fi/graphql'
+ schema: '../schema.graphql'
extensions:
  graphql-markdown:
    baseURL: '.'
    homepage: 'static/index.md'
    loaders:
-      UrlLoader: '@graphql-tools/url-loader'
+      GraphQLFileLoader: '@graphql-tools/graphql-file-loader'
    docOptions:
      frontMatter:
        pagination_next: null
        pagination_prev: null
    printTypeOptions:
      deprecated: 'group'

```

- `npm run doc`でドキュメントを生成し、ローカルサーバーを立ち上げて確認します。ブラウザが立ち上がり、schema.graphql通りのドキュメントになっていれば成功です。
    
    ```shell
    $ npm run doc
    $ npm run start
    ```

    ![スクリーンショット 2024-05-16 22.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/072c283c-fc28-65c0-9856-4a3a69aeade8.png)

GraphQL-Markdownの良いところとして、「[@specifiedBy](https://spec.graphql.org/draft/#sec--specifiedBy)が反映される」が挙げられます。いくつかドキュメント生成ツールを試しましたが、specifiedByが反映されたのはGraphQL-Markdownだけでした。

![スクリーンショット 2024-05-16 22.25.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/eb14bc79-2ed4-9e92-4243-088823e27e8b.png)

### カスタムディレクティブの反映

また、GraphQL-Markdownはカスタムディレクティブをドキュメントに反映させることができ、これも良かったです。

https://graphql-markdown.dev/docs/advanced/custom-directive/

例えばスキーマに以下を追加します。

```ts
directive @validate(description: String!) on INPUT_FIELD_DEFINITION

input ComicInput {
  title: String! @validate(description: "1000文字以内")
}

```

リファレンス通り、`docusaurus.config.js`を編集しdescriptorとtagを追加します。

```docusaurus.config.js
const { directiveDescriptor } = require("@graphql-markdown/helpers")

...

plugins: [
  [
    "@graphql-markdown/docusaurus",
    /** @type {import('@graphql-markdown/types').ConfigOptions} */
    {
      customDirective: {
        validate: {
          descriptor: (directive, node) =>
            directiveDescriptor(
              directive,
              node,
              "`${description}`",
            ),
            tag: (directive, node) => ({ text: directive.name, classname: "badge--info" }),
        }
      },
    },
  ],
],
```
`graphql-markdown/helpers`も必要そうなので入れます。

```shell
npm i @graphql-markdown/helpers
```

これで再度docs生成とサーバーを立ち上げ、確認します。

```shell
$ npm run doc
$ npm run start
```


![スクリーンショット 2024-05-17 20.51.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/77fd053d-830b-aa5f-3986-02add7e9b8a1.png)

![スクリーンショット 2024-05-17 20.59.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/a22e65fb-0e1f-c491-1ebd-0606e2aaca87.png)

カスタムディレクティブはタグとして反映されるようです。文字列も反映されるのはいい感じです。

### 最後に

本番で使う場合は、`npm run build`でHTMLの生成もできるので、生成したファイルを静的ホスティングして共有することもできそうです。