こんにちは! STORES でソフトウェアエンジニアをしている @m0nch1 です。
STORES ではいたるところで GraphQL が採用されており、今や STORES のものづくりにおいては欠かせないものになっています。
GraphQL 関連の記事がいくつかあるので抜粋して紹介しておきます。
今回はタイトルの通り、みんな大好き自動化の話です。
この記事が誰かの作業を少しでも短縮できるといいなと思っています!
手動でスキーマ同期する(自動化する前)
現在開発しているアプリケーションでは、Web フロントエンドとバックエンド間の通信に GraphQL を使用しています。バックエンドの開発には Ruby on Rails と graphql-ruby を使用しています。
API のスキーマはバックエンド側で生成され、それをフロントエンドが参照する形で動作しています。そのため、バックエンドのスキーマが更新された場合、フロントエンドもそれに合わせてスキーマを更新する必要があります。
手動でスキーマを更新する手順
- バックエンドのリポジトリを最新の状態にする
- ローカル環境でバックエンドのサーバーを起動
- ローカル環境の GraphQL サーバーから GraphQL の introspection の仕組みを使用してスキーマを取得
- graphql-codegen を使用してスキーマを生成
graphql-codegen でスキーマを生成する
graphql-codegen を使ってスキーマを生成するためには設定ファイルを用意する必要があります。
graphql-codegen は graphql-config もサポートしており、様々なファイル形式で設定ファイルを用意することができます。
.graphqlrc ファイルの例:
# .graphqlrc # ローカルで起動しているバックエンドサーバーのイントロスペクション(introspection)エンドポイント schema: https://localhost:3000/graphql/dev extensions: codegen: generates: ./schemas/example-api.graphql: plugins: - schema-ast
以下のように npm script を用意しておけば、npm run update-graphql-schema
を実行するだけでスキーマが生成されます。
// package.json 一部抜粋 { "scripts": { "update-graphql-schema": "graphql-codegen", } }
自動化したい...
このように、ローカル起動したバックエンドのスキーマに基づいてスキーマを生成するという手順は、バックエンドと一緒に開発している間は簡単です。
しかし、チーム開発においてバックエンドとフロントエンドのタスクを分割したり、API の異なる変更が各所で進行したりすると、ローカルでバックエンドを手元に持ってくるのが手間であったり、何より Pull Request がコンフリクトするという問題もおきました。
どうすれば自動化できるか
バックエンドで発生する API の変更を、フロントエンド側のリポジトリが自動で追随している状態を目指すため、以下のような手順を取ることにしました。
- サーバー側リポジトリのスキーマ更新を通知できるようにする
- Web フロントエンドのリポジトリで通知を受け取り、スキーマ更新の Pull Request を作成する
サーバー側リポジトリのスキーマ更新を通知できるようにする
GitHub Actions にはワークフローをトリガーするイベントが様々ありますが、repository_dispatch を使うことで他のリポジトリにイベントを通知することができます。
バックエンドのスキーマが main ブランチに push されたタイミングをきっかけにできれば良いので、以下のようなワークフローを用意すれば解決できます。
# schema-updated.yml name: Dispatch Schema Updated on: push: branches: - main paths: - app/graphql/example_api/schema.graphql jobs: dispatch-schema-updated: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/create-github-app-token@v1 id: app-token with: app-id: XXXXXX private-key: ${{ secrets.XXX_KEY }} owner: ${{ github.repository_owner }} repositories: frontend-repository-name - name: dispatch schema update uses: peter-evans/repository-dispatch@v3 with: token: ${{ steps.app-token.outputs.token }} repository: ${{ github.repository_owner }}/frontend-repository-name event-type: update-api-schema
Web フロントエンドのリポジトリで通知を受け取り、スキーマ更新の Pull Request を作成する
フロントエンド側のリポジトリでは、repository_dispatch イベントをトリガーとして受け取り、スキーマを更新する GitHub Actions を設定します。
repository_dispatch の types にサーバー側リポジトリで設定したイベントの event-type を指定することで、サーバー側のリポジトリでディスパッチしたイベントをトリガーにすることができます。
あとは、ジョブのなかでスキーマ更新の npm script を実行すればスキーマ更新の Pull Request が作成できます。
# update-schema.yml name: Create update schema Pull Request on: workflow_dispatch: // 手動実行もできるようにしておく repository_dispatch: types: [update-api-schema] jobs: update-api-schema: name: Update API schema runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v4 - run: corepack enable - uses: actions/setup-node@v4 with: node-version-file: '.node-version' cache: 'npm' - name: Install dependencies run: npm install - uses: actions/create-github-app-token@v1 id: app-token with: app-id: XXXXXX private-key: ${{ secrets.XXX_KEY }} owner: ${{ github.repository_owner }} - name: Update GraphQL Schema env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} run: npm run update-graphql-schema - uses: peter-evans/create-pull-request@v7 with: token: ${{ steps.app-token.outputs.token }} commit-message: Update API schema branch: update-api-schema branch-suffix: timestamp delete-branch: true title: Update API schema body: | This Pull Request updates the API schema. ref: https://github.com/organization-name/repository-name/blob/main/app/graphql/example_api/schema.graphql team-reviewers: xxx/review-sitehosii-team
.graphqlrc を修正
repository_dispatch によって、自動でスキーマを更新する Pull Request を作成することはできるようになりましたが、実はこれだけではうまくいきません。
npm run update-graphql-schema
を実行すると .graphqlrc の schema フィールドに指定しているパスへスキーマを取りに行くのですが、冒頭に記載した設定のままでは開発サーバのエンドポイントを設定しているため、当然 GitHub Actions の実行環境からこのパスへアクセスすることはできません。
# .graphqlrc 再掲 # GitHub Actions は以下のパスにアクセスできない schema: https://localhost:3000/graphql/dev extensions: codegen: generates: ./schemas/example-api.graphql: plugins: - schema-ast
実はこの schema フィールドには GitHub リポジトリのパスを指定することができます。(知らなかった)
ref: https://the-guild.dev/graphql/codegen/docs/config-reference/schema-field#github
※ もちろん、プライベートリポジトリにも対応しています。
以下のように schema フィールドを GitHub リポジトリのパスに設定することでスキーマを取得することができるようになります。
# .graphqlconfig projects: default: schema: github:some-organization/backend-repository-name#main:app/graphql/example_api/schema.graphql extensions: codegen: generates: ./schemas/example-api.graphql: plugins: - schema-ast dev: schema: https://localhost:3000/graphql/dev extensions: codegen: generates: ./schemas/example-api.graphql: plugins: - schema-ast
projects の dev については、開発中のスキーマをローカルで取得したい時などに使用したいため project を分けて用意しました。
以下のように使い分けができます。
// package.json 一部抜粋 { "scripts": { "update-graphql-schema": "graphql-codegen", "update-graphql-schema:dev": "graphql-codegen --project dev" } }
まとめ
以上、GitHub Actions をうまく使うだけで簡単にスキーマ更新の自動化をすることができました。
これによりスキーマ更新をしたメンバーがスキーマを直ちに最新化する動きができ、スキーマ部分でコンフリクトすることも無くなりました!
自動化や業務効率化はちりつもですが、確実に有意義な時間を産む方法だと改めて感じました。
もっともっと GitHub Actions を使いこなしていきたいですね!