尝试在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。

执行环境

versionruby2.6.3rails5.2.3gem graphql1.11.6gem graphql-rails1.7.0

操作步驟

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
}
image.png

用样本数据试一试

由于年底将至,我将结合故乡捐赠的背景,创建三个相关的样本数据来进行说明。

    • local_governments: 自治体

id: 自治体ID

suppliers: 返礼品事業者

id: 返礼品事業者ID
local_government_id: 自治体ID

return_gifts: 返礼品

id: 返礼品ID
supplier_id: 返礼品事業者ID
name: 返礼品名

IMG_1926.JPG

数据库架构定义

首先是从架构定义开始。
我们将按照每个表的对应形式进行定义。

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 的故事。(还有演示!)

广告
将在 10 秒后关闭
bannerAds