使用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の型定義ファイルへコンバート
フロントアプリケーションにて利用可能に
大致上的情况就是这样了。
现状与当时相比并没有太大变化,但如果有更好的实践方式,我会非常乐意听取您的反馈。