用Rails构建GraphQL服务器的体验🤔💭

不论之前有没有阅读过关于在Rails上创建Web API的方法和优点的文章,都可以进行搭建。

我使用Rails搭建了一个GraphQL服务器,并实现了基本的查询和变更功能。

给定情况下

    • GraphQLをある程度知っていないとコードの理解は困難です。m(_ _)m

 

    GraphQLが何なのかは説明しません。ある程度、GraphQLがどういうものかを知っている前提で進めます。

请务必参考官方网站或其他相关信息了解产品规格。

    HTTP リクエストメソッドについては説明しません。

请务必参考MDN(https://developer.mozilla.org/ja/docs/Web/HTTP/Methods)。

    HTTPリクエストボディの説明はしていません。

这本IT用语词典能够让人即使在不明确的情况下也能够有一种”好像懂了”,”虽然不明白但是感觉理解了”的错觉。
这个网站适合初学者,用语解释得很容易理解。

    DBにbooksテーブルがある状態で始めます。

这是一个具有先前字符串的标题和正文栏位的表格。

要将Rails转换为GraphQL服务器,应该怎么做呢?

本次先准备Rails的API模式。(在前一篇文章中已经准备好了。)

还有完整版本的源代码可用。

然后,在Gemfile中添加graphql-ruby,并运行bundle install将其转化为GraphQL服务器。

由于API服务器已经准备就绪,因此可以从安装Gem开始进行进一步操作。

我们将使用Rails来创建GraphQL服务器。

在Gemfile中新增以下内容,并执行安装。

gem 'graphql'
$ bundle install

执行以下命令将生成GraphQL相关文件群。

$ rails g graphql:install

Running via Spring preloader in process 35807
      create  app/graphql/types
      create  app/graphql/types/.keep
      create  app/graphql/graphql_server_sample_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"
Skipped graphiql, as this rails project is API only
  You may wish to use GraphiQL.app for development: https://github.com/skevy/graphiql-app
      create  app/graphql/types/node_type.rb
      insert  app/graphql/types/query_type.rb
      create  app/graphql/types/base_connection.rb
      create  app/graphql/types/base_edge.rb
      insert  app/graphql/types/base_object.rb
      insert  app/graphql/types/base_object.rb
      insert  app/graphql/types/base_union.rb
      insert  app/graphql/types/base_union.rb
      insert  app/graphql/types/base_interface.rb
      insert  app/graphql/types/base_interface.rb
      insert  app/graphql/graphql_server_sample_schema.rb

定义 object type

Object type决定了返回的数据是什么形式。

$ rails g graphql:object Book
Running via Spring preloader in process 97118
      create  app/graphql/types/book_type.rb

将创建一个名为 app/graphql/types/book_type.rb 的文件。

打开文件后,应该会有以下字段定义。

在创建Book模型时,字段将被自动定义。

module Types
  class BookType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: true
    field :body, String, null: true
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
  end
end

定义一个查询

module Types
  class QueryType < Types::BaseObject
    # bookのデータがあれば[本の情報、本の情報, ...]という形式でデータを返します。
    # 角括弧内でnull: trueしているので空の[ ]を返してもOKにしてます。
    # null: falsede必ずデータは何かしら返さなきゃいけないようにしています。(なかったらエラー)
    field(:books, [BookType, null: true], null: false) do
      description 'fetch all books'
    end

    # bookのデータを一つ返すようにしています。
    # null: falseなので必ずデータ返さなきゃいけないようにしてます。(なかったらエラー)
    field(:book, BookType, null: false) do
      description 'find a book by id'
      argument :id, ID, required: true
    end

    def books
      Book.all
    end

    def book(id:)
      Book.find(id)
    end
  end
end

现在准备好了从这里获取数据。现在让我们使用API开发工具进行验证。

我个人更喜欢用Postman,因此会使用Postman来确认,虽然也可以使用Gem graphiql-rails来在Graphiql等工具中进行确认。

Postman是一款能够帮助开发API的工具。

通过指定URI执行通信请求,并且可以确认返回的数据是什么样的,嗯,可以进行各种确认。

让我们立即尝试通过指定GraphQL查询来获取数据,查看是否成功。

执行Query

    • rails s でサーバーを起動します。

 

    • HTTP verbを POSTにします。

 

    • GraphQLのラジをボタンを選択します。

 

    HTTP リクエストメッセージボディクエリを記述します。
スクリーンショット 2021-12-29 20.42.58.png

让我们在另一个查询中尝试通过GraphQL请求数据。

获取id为1的书籍的标题。

スクリーンショット 2021-12-29 20.44.53.png

让我们一次执行多个查询。
我们将获取所有图书的ID以及ID为1的图书的全部信息。

スクリーンショット 2021-12-30 17.15.36.png

这样一来,你直观地知道查询会返回什么数据了呢!

此外,由于可以只提取所需数据,因此不再会过度获取无用数据。

Mutation的定义

刚才我们使用查询(Query)来获取数据,但是数据的创建、更新和删除将使用“变更”(Mutation)来执行。

我們將開發創建和獲取數據的功能。

本次我们将定义用于创建书籍的变异(mutation)。

执行以下操作,开始实现名为create_book的mutation。

$ rails g graphql:mutation create_book

Running via Spring preloader in process 61114
       exist  app/graphql/mutations
   identical  app/graphql/mutations/.keep
   identical  app/graphql/mutations/base_mutation.rb
   identical  app/graphql/types/mutation_type.rb
add_root_type  mutation
      create  app/graphql/mutations/create_book.rb
        gsub  app/graphql/types/mutation_type.rb

我注意到已经创建了一个名为 app/graphql/mutations/create_book.rb 的文件。

我們來修改如下。 .)

module Mutations
  class CreateBook < BaseMutation
    # 返却されるデータの形を定義しています。
    field :book, Types::BookType, null: false

    # GraphQL Variableの情報を定義してます。
    # これがないと作成時の情報も利用できないです。
    argument :body, String, required: true
    argument :title, String, required: true

    # Resolverで返却したいデータをreturnします。
    def resolve(**attributes)
      # attributesには次のHashが渡ってきます。(attributesという名前以外でもOKです。)
      # {:body=>"GraphQL理解しないとGraphql-rubyわからんね", :title=>"GraphQLな奴"}
      book = Book.create!(attributes)
      { book: book } # このデータが返却されます。(Rubyはreturn省略できます。)
    end
  end
end

在字段:book中,定义为Types::BookType,null值为false,但最终会返回以下数据。

{
    "data": {
        "createBook": {
            book: {
              "title": "GraphQLな奴",
              "body": "GraphQL理解しないとGraphql-rubyわからんね"
            }
        }
    }
}

执行突变

スクリーンショット 2021-12-30 1.34.13.png

顺便问一句,如果想要以以下的方式接收数据应该怎么做呢?

{
    "data": {
        "createBook": {
            "title": "GraphQLな奴",
            "body": "GraphQL理解しないとGraphql-rubyわからんね"
        }
    }
}

返回的数据形式是通过字段(field)进行定义的。

所以只需要更改返回的字段即可。

module Mutations
  class CreateBook < BaseMutation
    # 既にapp/graphql/types/book_type.rbで定義しているfield群を利用します。
    # その場合はfieldではなくてtypeで呼び出します。
    type Types::BookType

    argument :body, String, required: true
    argument :title, String, required: true

    def resolve(**attributes)
      Book.create!(attributes)
    end
  end
end

当我们实际查询时,会有这样的感觉

スクリーンショット 2021-12-30 1.46.46.png

我会简要解释代码的流程。

    • Mutationが GraphQL::Schema::RelayClassicMutation を継承してると、デフォルトでInput objectが自動で定義されるようになっています。クエリにもinput型の定義が必要になります。(GraphQL Rubyの仕様です。)

 

    input型を定義して、それに相当するデータもGraphQL Variableに指定してクエリを実行します。

在说到input类型时,使用Rails的人可能会比较容易理解提及的是Strong Parameters的类型…? ??

    fieldで定義した通りのデータがResolverから(resolveメソッドから)返ってきます。

最后返回数据的方法是resolve。它被称为解析器。

    今回はbookのtitleとbodyだけを取得するようにしているので、title, bodyの情報が返ってきました。

在执行查询时,只指定了标题(title)和正文(body)字段,所以不会返回其他数据呢。

スクリーンショット 2021-12-30 2.24.49 2.png

对于那些觉得找到多种定义很麻烦的人!!!

如果你想在没有指定输入类型的情况下执行查询,只需不继承 GraphQL::Schema::RelayClassicMutation 就可以了。

module Mutations
  # 継承元を変更します。
  class CreateBook < GraphQL::Schema::Mutation
    field :book, Types::BookType, null: false

    argument :title, String, required: true
    argument :body, String, required: true

    def resolve(**attributes)
      book = Book.create!(attributes)
      { book: book }
    end
  end
end

在这个状态下执行查询时,无需指定input类型或GraphQL变量即可执行查询。

スクリーンショット 2021-12-30 16.42.14.png

但是,我认为使用input类型会更好,因为它可以像strong parameters一样重复使用,并且在类型方面更方便。

GraphQL有什么优点呢???

我们迄今为止已经使用Query和Mutation来获取和创建数据。

GraphQLのいいところはやはり直感的にどういうデータが返ってくるかが分かりやすい。

データの型も指定できるのでどういう型のデータが来るかも分かりやすいです。

本当に必要な必要最小限のデータだけを柔軟に取得できる

クエリ実行時にbookのidだけ必要であればクエリのfieldにidだけ書けば良いのです。

一つのエンドポイントで、かつ一度のリクエストで色々なデータにアクセス可能

この記事では全データの取得、データの一つを取得、データの作成をたった一つのエンドポイントで成し遂げました。

http://localhost:3000/graphql だけでやりとげました。たくさんRouting定義しなくてもいいわけです。
クエリは一度に複数指定も可能でした。

除此之外,还有许多其他优点,所以我们开始创建这样的API服务器。

从前端怎样执行查询呢?

使用针对Apollo Client 或 GraphQL专用的库会更好。

使用Fetch API或Axios,您可以将查询包含在HTTP POST方法请求的主体中,而无需使用库。

简单来说,只要能进行HTTP请求,就可以执行查询(好像甚至不仅限于HTTP)。

有关Apollo Client的内容超出了文章范围,所以我将不再详细介绍。m(_ _)m

美妙的API,讓GraphQL的生活變得更加精彩٩(ˊωˋ)و✧

广告
将在 10 秒后关闭
bannerAds