21
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Linc'wellAdvent Calendar 2019

Day 3

Rails と React+TypeScript でのGraphQLスキーマ運用プラクティス

Last updated at Posted at 2019-12-02

Linc'well Advent Calendar3日目の記事です。

当社が展開するクリニックグループである CLINIC FOR の予約システム では一部機能にGraphQLを導入しているのですが、スキーマの管理等も踏まえてどのように構築し、運用したかについて紹介したいと思います。

※ ちなみに構築したのは2019/3~4頃であり、その点は割り引いてご覧ください :pray_tone2:
今だともっと良さげなプラクティスがあるかもしれません。

また、予約システム全体としてはモノリシックなRailsアプリケーションになっているのですが、GraphQLを導入した一部機能ではフロントエンドがTypeScript+Reactで構築されており、ruby側で定義されたスキーマ情報はTSの型定義へスムーズに繋げる必要がありました。

スキーマファイルの生成

GraphQLには様々な実装があるかと思いますが、今回rmosolgo/graphql-ruby というgemを利用しました。

こちらのgemはスキーマの定義ファイルを直接記述せず、個別のクエリや型の実装から最終的なスキーマを出力する code-first なアプローチを取っています。

なので処理を先に記述し、それを参照することで型定義が決定される、という順番になるのですが、最終的な定義の情報は、Schemaのクラスに生えている以下のメソッドで文字列として全出力され、その戻り値をファイル出力してあげることでスキーマファイルが出来上がります。

GraphQL::Schema#to_definition

今回は上記メソッドを以下のようなrakeタスクに組み込み、差分が生じたらファイル出力の上でコミットしていくという運用で行くことにしました。

# rake graphql:dump_schema
namespace :graphql do
  task dump_schema: :environment do
    schema_definition = LincwellSchema.to_definition
    schema_path = 'frontend/src/generated/schema.graphql'
    File.write(Rails.root.join(schema_path), schema_definition)
    puts "#{schema_path} updated."
  end
end

CIでのスキーマ定義チェック

定義ファイルが最新のものかチェックするため、rspecにてスキーマファイルの最新チェックも用意します。

describe 'Validate lastest-veresion' do
  let(:dump_path) { Rails.root.join('frontend', 'src', 'generated', 'schema.graphql') }
  let(:current_definition) { File.read(dump_path) }

  subject { described_class.to_definition }

  it { is_expected.to eq(current_definition) }
end

簡易な実装ですが、CIが回るたびにチェックが入るので、少なくともこれで更新忘れはなくなり、実装とスキーマの乖離を防止することができたのではないかと思います。

スキーマ情報からTSの型をつくる

続いてはこうして出来上がったスキーマ情報をフロントエンド側へ連携させていきます。

出力されたスキーマ情報をもとにTSの型を作りたいのですが、さすがに手ずから行うのは厳しいので graphql-codegenを用いて自動化することにします。

# codegen.yml
overwrite: true
schema: "src/generated/schema.graphql"
documents: "src/libs/queries/*.ts"
generates:
  src/generated/graphql.tsx:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-react-apollo"
  src/generated/graphql.schema.json:
    plugins:
      - "introspection"
"scripts": {
  "generate": "graphql-codegen --config codegen.yml"
}

上記設定の上、 npm run generate コマンドを実行します。

そうするとrailsから出力されたスキーマファイルを元に、 src/generated/graphql.tsx にTSの型定義が自動生成されるようになります。ここまで繋がると気持ちイイですね!

これでReactコンポーネントから自由に任意のクエリやオブジェクトをTSの型として利用できるようになりました。Rails側で定義されている graphql/types と乖離しないため、非常に楽に、かつ楽しく開発を進めることができます。

{
    "paths": {
      "Components/*": ["src/components/*"],
      "Styles/*": ["src/styles/*"],
      "Libs/*": ["src/libs/*"],
      "Generated/*": ["src/generated/*"]
    }
  }
}

tsconfig.jsonで上記のようにaliasを切り、Componentで以下のように呼び出せます。

import * as React from "react";
import { useQuery } from "react-apollo-hooks";
import styled from "styled-components";
import { CancelRate } from "Generated/graphql";

めでたしめでたし :tada:

ちなみにフロントエンド側の型生成まわりに関しては以下の記事を大変参考にさせて頂きました。

投稿時期も非常にタイムリーで、もしこのエントリがなかったらと思うと(ry

まとめ

  • code-firstな graphql-ruby の利用
  • #to_definition をrake経由で実行し、型定義の .graphql ファイルをフロントへ配置
  • rspec にて最新dumpの検査
  • graphql-codegen にてTSの型定義ファイルへコンバート
  • フロントアプリケーションにて利用可能に

だいたいこんな感じになりました。
現状もこの当時からあまり大きく変わっていませんが、もしより良いプラクティスがあればフィードバック頂けたら幸いです。

21
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?