尝试在Rails上使用GraphQL
首先
圣诞快乐!我曾以为圣诞节是庆祝耶稣基督的生日,但最近才知道它只是庆祝他的诞生,并不一定是他的生日。
圣诞节(英文:Christmas)的意思是“基督弥撒”,是一些教派庆祝耶稣基督降生的节日[1]。它只是庆祝耶稣基督的诞生,而不是他的生日[2]。
每年12月25日庆祝,但使用儒略历的正教会在格里高利历的1月7日庆祝圣诞节[3][4]。然而,基督教最重要的节日不是圣诞节,而是复活节[5][6][7][8]。
在基督教之前的犹太教历法、罗马帝国历法以及它们继承下来的教会历法中,与现代常用时间不同,日落被视为一天的分界线,因此从12月24日傍晚称为圣诞前夜直到12月25日早上,它在教会历法上被计算为同一天[9]。因此,教会历法中的圣诞节是指“从12月24日日落到12月25日日落”的时间段。
由于偶然在圣诞节当天负责编写,本来打算写一些与圣诞有关的话题,但我完全想不出来,所以只能正常撰写文章了。
這是Hamee Advent Calendar 的第 25 篇文章。
背景信息
我平时从事故乡纳税相关的工作。目前,应用工程师团队有两个人。我们正在开发多个应用程序,而且在API开发方面遇到了一些挑战。
-
- エンドポイントがたくさん。IFを実装するたびに都度作成していて、管理しにくくなってきている
- スピード重視・少人数のため、APIドキュメント管理できていない
我想尝试一下GraphQL,虽然现在有点晚了,但还是决定试一试。
GraphQL是什么?
由于GraphQL的介绍已经被许多人写成文章,所以我会简单地总结一下。(这里不会解释与REST的区别)
GraphQL是由Facebook开发的用于Web API的规范,由”查询语言”和”模式语言”组成。
它具有以下特点:
-
- スキーマ(Web APIの仕様)を構築し、クエリでどのようなデータを取得するかを表現
-
- 単一のエンドポイント(/graphql)のみ。ほしいデータはhttp bodyに明記して取得可能
-
- GraphiQLという専用のIDEを使えば、スキーマ等のドキュメントも確認可能
- クエリ言語にはデータ取得系のquery、データ更新系のmutation、pub/subモデルでサーバーサイドのイベントを受け取るsubscriptionがある
我非常感谢以下的文章,它给了我很多参考:
《GraphQL》全面入门 ─ 与REST的比较,从API和前端的实现中学习
与Web API初学者一起学习GraphQL
试试看你好世界
本次我們將使用提供的 Ruby 函式庫 graphql 和 graphql-rails 來嘗試使用 GraphQL。
执行环境
操作步驟
gem 'graphql'
group :development, :test do
gem 'graphiql-rails'
end
在将Gemfile写入后,请执行以下命令。
执行后,将生成以下文件组。
$ bundle install
$ rails generate graphql:install
$ bundle exec rails g graphql:install
create app/graphql/types
create app/graphql/types/.keep
create app/graphql/app_schema.rb
create app/graphql/types/base_object.rb
create app/graphql/types/base_argument.rb
create app/graphql/types/base_field.rb
create app/graphql/types/base_enum.rb
create app/graphql/types/base_input_object.rb
create app/graphql/types/base_interface.rb
create app/graphql/types/base_scalar.rb
create app/graphql/types/base_union.rb
create app/graphql/types/query_type.rb
add_root_type query
create app/graphql/mutations
create app/graphql/mutations/.keep
create app/graphql/mutations/base_mutation.rb
create app/graphql/types/mutation_type.rb
add_root_type mutation
create app/controllers/graphql_controller.rb
route post "/graphql", to: "graphql#execute"
gemfile graphiql-rails
route graphiql-rails
Gemfile has been modified, make sure you `bundle install`
另外,还可以通过专用的IDE或/graphiql进行确认。
(在我的情况下,初始界面只能看到QUERY VARIABLES,但重新加载后可以显示)
首先执行以下查询(左侧),如果执行结果正确显示(右侧),则表示Hello world成功。
query {
testField
}

用样本数据试一试
由于年底将至,我将结合故乡捐赠的背景,创建三个相关的样本数据来进行说明。
-
- local_governments: 自治体
id: 自治体ID
suppliers: 返礼品事業者
id: 返礼品事業者ID
local_government_id: 自治体ID
return_gifts: 返礼品
id: 返礼品ID
supplier_id: 返礼品事業者ID
name: 返礼品名

数据库架构定义
首先是从架构定义开始。
我们将按照每个表的对应形式进行定义。
module Types
class LocalGovernmentType < Types::BaseObject
field :id, ID, null: false
field :suppliers, [Types::SupplierType], null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
module Types
class SupplierType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
def name
object.info['supplier_name']
end
field :return_gifts, [Types::ReturnGiftType], null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
在resolver中,对象通过变量object进行传递。虽然suppliers.name本来不存在,但通过传递以json格式定义的数据来使用它。我认为可以像使用装饰器一样使用它。
module Types
class ReturnGiftType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
field :supplier, Types::SupplierType, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
查询
我想从样本数据中获取与特定自治体相关的回礼品商家和所有回礼品。
定义
我们将定义查询和创建解析器。
module Types
class QueryType < Types::BaseObject
field :local_government, LocalGovernmentType, null: false, description: '自治体情報' do
argument :id, Int, '自治体ID', required: true
end
def local_government(id:)
LocalGovernment.find_by(id: id)
end
end
end
我们会确保地方政府信息可以通过ID进行条件筛选。
执行
query {
localGovernment(id: 1){
id
suppliers{
id
returnGifts{
id
name
}
}
}
}
执行以上查询后,我们可以看到返回以下响应。
{
"data": {
"localGovernment": {
"id": "1",
"suppliers": [
{
"id": "1",
"name": "返礼品事業者X",
"returnGifts": [
{
"id": "1",
"name": "返礼品A"
},
{
"id": "4",
"name": "返礼品B"
},
{
"id": "5",
"name": "返礼品C"
}
]
},
{
"id": "5",
"name": "返礼品事業者Y",
"returnGifts": [
{
"id": "11",
"name": "返礼品I"
},
{
"id": "12",
"name": "返礼品J"
}
]
}
]
}
}
}
相关信息也能很好地获取到。
突变
我想添加特定回复礼品提供商的回礼品信息。
操作流程为:创建Mutation类→定义Mutation→执行查询。
突变(Mutation)类
module Mutations
class CreateReturnGiftMutation < Mutations::BaseMutation
argument :supplier_id, Int, required: true
argument :name, String, required: true
field :return_gift, Types::ReturnGiftType, null: true
field :errors, [String], null: false
def resolve(supplier_id:, name:)
return_gift = ReturnGift.new(
supplier_id: supplier_id,
name: name
)
if return_gift.save
{
return_gift: return_gift,
errors: []
}
else
{
return_gift: nil,
errors: return_gift.errors.full_messages
}
end
end
end
end
突變的定義是什麼?
module Types
class MutationType < Types::BaseObject
field :create_return_gift, mutation: Mutations::CreateReturnGiftMutation
end
end
执行
mutation {
createReturnGift(input: {
supplierId: 5
name: "追加した返礼品"
})
{
returnGift{
id
supplier{
id
name
returnGifts{
id
name
}
}
}
}
}
执行上述查询后,我们可以发现将返回以下响应。
还会生成回礼品数据。
{
"data": {
"createReturnGift": {
"returnGift": {
"id": "24",
"supplier": {
"id": "5",
"name": "返礼品事業者Y",
"returnGifts": [
{
"id": "11",
"name": "返礼品I"
},
{
"id": "12",
"name": "返礼品J"
},
{
"id": "24",
"name": "追加した返礼品"
}
]
}
}
}
}
}
最后
这次我们尝试使用GraphQL来获取和创建数据。
我对于自动生成文档(Graphiql太方便了)和单一终端点以及(如果正确命名的话)直观易懂的查询等等感到相当震撼。
除此之外,我也希望学习一些最佳实践,比如如何使用graphql-batch以及类型定义等。
Hamee 2020年的圣诞倒计时活动辛苦啦!明年再一起努力吧!
另外,明年度的故乡纳税抵免截止日期为12月31日。如果您还没有完成,赶紧行动吧。
请提供所需重述的具体句子。
【Rails】在 graphql-ruby.org 上创建 API
使用 GraphQL Ruby 的方式(基础篇)
讲述了 Ruby on Rails 工程师使用 GraphQL 轻松创建最强的 WebAPI 的故事。(还有演示!)