graphql-tools + GitHubでGraphQLサーバとスキーマ定義を分離する

## 目次

1. [目次](#目次)
1. [前書き](#前書き)
1. [本題](#本題)
    1. [GraphQLサーバとスキーマ定義を分離する](#graphqlサーバとスキーマ定義を分離する)
        1. [スキーマ定義用のGitHubリポジトリを作成する](#スキーマ定義用のgithubリポジトリを作成する)
        1. [GraphQLサーバでGitHubからスキーマ定義を読み込む](#graphqlサーバでgithubからスキーマ定義を読み込む)
1. [まとめ](#まとめ)

## 前書き

### 経緯
GraphQLを使ってアプリケーション開発を試していたところ、
GraphQLサーバとスキーマ定義が一体化していると開発しづらいと感じたため、
分離する方法を調査して試してみました

### 対象読者

- GraphQLサーバとスキーマ定義を分離したい人
- Node.jsを使用してGraphQLサーバを構築したい人
    - graphql-toolsを使用している/使用を検討している人
- GitHubを使用してリポジトリを管理している人

### 今回使用するものの説明
簡単にですが、今回使用する技術要素について以下に記載します

#### GraphQLとは
[GraphQL](https://graphql.org)とはデータソースと通信するためのクエリ言語であり、以下のような形式で記述します

``` graphql
# 型定義
type Exmaple {
    id: ID!
    requiredString: String!
    string: String
}

# 参照
type Query {
    read(id: ID!, string: String): Exmaple
}

# 作成/更新/削除
type Mutation {
    create(id: ID!, string: String): Exmaple
    update(id: ID!, string: String): Exmaple
    delete(id: ID!): Exmaple
}

```

スカラー型を組み合わせて独自の型を作成したり、
Queryにデータ参照機能のインタフェース定義、
Mutationにデータ編集機能のインタフェース定義を作成することができます

またGraphQLのスキーマ定義そのものがインターフェース定義書となるため、ドキュメント作成の手間を省くことができます

**GraphQLサーバ**とはGraphQLでやり取りできるサーバアプリケーションを指します

#### graphql-toolsとは
[graphql-tools](https://www.graphql-tools.com)とはNode.jsのパッケージとして作成されたGraphQLのユーティリティーツール群です

今回、分離したスキーマ定義を読み込むために使用します

## 本題

### GraphQLサーバとスキーマ定義を分離する

#### スキーマ定義用のGitHubリポジトリを作成する
まず分離したスキーマ定義を管理するためにGitHubにリポジトリを作成します

リポジトリを作成する際は、機能単位でスキーマ定義自体も分離させ、別々のリポジトリで管理します

![read_schema.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730831/3ea92efc-67bb-2601-6bdc-a0c3ac049b60.png)

GraphQLのスキーマ定義は参照機能をQuery型、編集機能をMutation型に定義しなければならないため、通常1ファイルにまとめて定義する必要があります

しかし、graphql-toolsを使用することで、別々のスキーマ定義をマージして使用することができます

こうすることで以下のメリットがあります
- スキーマ定義が機能単位で分割されるため拡張/修正しやすくなる
    - 新規追加時/修正時に他の定義への影響をなくすことができる
- スキーマ定義を利用する側のアプリケーションが使用する定義を選択できる
    - マイクロサービスアーキテクチャでアプリケーションを構築することも可能になる

#### GraphQLサーバでGitHubからスキーマ定義を読み込む
スキーマ定義を管理するリポジトリを作成できたら、GraphQLサーバ側にスキーマ定義を取得するための処理を追加します

graphql-toolsを使用したいので、GraphQLサーバはNode.jsで作成します
本記事作成時のNode.jsのバージョンは以下になります

```
node -v                                                     
v16.17.1
npm -v
8.15.0
```

まずは必要なライブラリをインストールしましょう
GitHubからスキーマ定義を取得したいので、**github-loader**と**load**ライブラリを使用します

```
npm i @graphql-tools/github-loader @graphql-tools/load
```

インストールしたgithub-loaderとloadライブラリを使用してスキーマ定義ファイルを取得し、読み込みます
尚、セキュリティ情報となる箇所は環境変数から取得するように記載しています

``` javascript
import { GithubLoader } from '@graphql-tools/github-loader';
import { loadSchema } from '@graphql-tools/load';

const GIT_TOKEN = process.env.GIT_TOKEN;
const USER_NAME = process.env.USER_NAME;

const typeDefs = await loadSchema(
      [
        `github:${USER_NAME}/feature-a-schema#v1:schema.graphql`,
        `github:${USER_NAME}/feature-b-schema#v1:schema.graphql`,
        `github:${USER_NAME}/feature-c-schema#v1:schema.graphql`
      ],
      {
        loaders: [new GithubLoader()],
        token: GIT_TOKEN
      }
    );

```

スキーマを読み込むのはloadライブラリの**loadSchema**メソッドで、loaderの指定が必要なため、**github-loader**を指定します

github-loaderでのスキーマポインター（スキーマ定義が配置されている場所）の記述方法は以下の通りです

``` javascript
    `github:${リポジトリのオーナー名}/${リポジトリ名}#${ブランチ名 or タグ}:${スキーマ定義ファイル名}`
```

このようにGitHubのリポジトリとスキーマ定義ファイルを指定することで外部で管理しているスキーマ定義を読み込むことができます
また、タグも指定できるのでスキーマ定義が更新されたとしてもタグを変更しなければアプリケーション側には影響が出なくなります

読み込むスキーマ定義は1度に複数記載することが可能です
読み込まれた定義は自動的にマージされて1つのスキーマ定義として使用できます

読み込んだスキーマ定義を[Apollo Server](https://www.apollographql.com)に渡してGraphQLサーバを起動させることもできます

これによって、スキーマ定義の開発とGraphQLサーバの開発を分離することができました

## まとめ

- github-toolsを使用することでGraphQLサーバとスキーマ定義を分離できる
    - 分離することでスキーマ定義を変更しやすくなる
    - スキーマ定義自体も分割管理できるようになる
- github-loaderを使用することでGitHubから直接スキーマ定義を読み込むことができる

まだ個人開発でしか試せていないですが、チーム開発でメリットを発揮しそうなので機会があれば取り入れてみたいと思います