使用Rails和React+TypeScript实施GraphQL模式管理实践

这是Linc’well Advent Calendar的第三天的文章。

我想介绍一下我们公司旗下 CLINIC FOR 诊所集团的预约系统,其中一些功能我们引入了 GraphQL。我希望分享一下我们是如何构建和运营这个系统,包括模式管理等方面的内容。

顺便提一下,这个构建是在2019年3月至4月左右完成的,请理解其中的时间差。现在可能有更好的实践方法。

此外,预约系统整体上是一个庞大的Rails应用程序,但在引入GraphQL的某些功能中,前端是由TypeScript+React构建的,而在ruby端定义的模式信息需要与TS的类型定义平稳连接。

生成一个模式文件

在GraphQL中,我认为有许多不同的实现方式,但这次我使用了一个叫作”rmosolgo/graphql-ruby”的gem。

这个gem采用了一种code-first的方法,不需要直接编写模式定义文件,而是通过单独的查询和类型实现来输出最终的模式。

因此,处理被首先描述,并通过引用它来确定类型定义的顺序,但最终定义的信息将作为字符串通过以下方法在Schema类中生长,并将其返回值输出到文件中,从而生成模式文件。

GraphQL::Schema#to_definition的意思是将GraphQL模式(Schema)转换为定义的形式。

我們決定將上述方法結合在下面的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输出模式文件时,将会根据此文件自动生成TS类型定义,并保存在src/generated/graphql.tsx中。达到这个程度时,感觉真棒呢!

现在,我们可以在React组件中自由地使用任意的查询和对象作为TypeScript类型。这样做可以确保与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";

恭喜恭喜

顺便说一下,关于前端类型生成方面,我参考了以下这篇文章。

如果没有这篇投稿的话,我觉得它的提交时间非常及时。

总结

    • code-firstな graphql-ruby の利用

#to_definition をrake経由で実行し、型定義の .graphql ファイルをフロントへ配置

rspec にて最新dumpの検査

graphql-codegen にてTSの型定義ファイルへコンバート
フロントアプリケーションにて利用可能に

大致上的情况就是这样了。
现状与当时相比并没有太大变化,但如果有更好的实践方式,我会非常乐意听取您的反馈。

广告
将在 10 秒后关闭
bannerAds