当前位置: 首页 > 工具软件 > graphql-faker > 使用案例 >

Rails使用GraphQL

邵伟
2023-12-01

GraphQL是API的查询语言,不依赖任何前端后台技术。在服务器端有很多种实现。

本例使用的是graphql-ruby(rails) ,用来解析传入的查询,并进行数据库调用,响应返回JSON,替换rails的api。

 

首先了解下graphql的几个组成部分

  1. Queries:从API获取特定的数据。将查询设置为只读,就像Rest里面的Get,但是查询不仅是Get。
  2. Mutations: 突变,对API数据的修改,比如:create、update、destroy。
  3. Types: 类型,用于定义数据类型,或者定义Rails模型model,类型包括根据Queries/Mutations中的请求相应数据的字段和函数。类型也可以是静态的,比如:String或ID,这些内置在服务器的数据库中。
  4. Fields: 字段,表示给定类型的属性
  5. Functions: 方法、功能,给上面的字段提供数据,

上面的5个部分配合工作,完成各种API的功能。

创建RailsAPI

创建一个rails api的项目来体验下graphql。

rails  new graphql_api  --api
rails g model User email:string name:string
rails g model Book title:stirng  summary:string  user:belongs_to
rails db:migrate

在user.rb里面加入 has_many :books

可以通过faker创建一些数据,比如:

5.times do
  user = User.create(name: Faker::Name.name, email: Faker::Internet.email)
  5.times do
    user.books.create(title: Faker::Book.title)
  end
end

rake db:seed

 

生成GraphQL文件

 

rails generate graphql:install
bundle
rails generate graphql:object user
rails generate graphql:object book

创建graphql目录和2个新的自定义类型User和Book

  ├─ controllers
+ │  └─ graphql_controller.rb
+ ├─ graphql
+ │  ├─ mutations
+ │  ├─ rails_graphql_demo_schema.rb
+ │  └─ types
+ │     ─ base_enum.rb
+ │     ─ base_input_object.rb
+ │     ─ base_interface.rb
+ │     ─ base_object.rb
+ │     ─ base_scalar.rb
+ │     ─ base_union.rb
+ │     ─ book_type.rb
+ │     ─ mutation_type.rb
+ │     ─ query_type.rb
+ │     ─ user_type.rb

同时也会创建app/controllers/graphql_controller.rb#execute,作为api访问的入口,开发环境增加一个graphiql-rails的gem包

# routes.rb

Rails.application.routes.draw do
  if Rails.env.development?
    mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "graphql#execute"
  end
  post "/graphql", to: "graphql#execute"
end

现在启动项目,访问http://localhost:3000/graphiql,就可以看到测试api接口的页面

 

我们还要创意一系列类型Type,便于GraphQL知道,GraphQL的类型(Tyep)等同于API的Model,也要指定字段(Fields)、方法(functions)等用于返回到客户端的应用程序。

User和Book的Type

每个字段都有个‘类型’以及是否允许为null,告诉graphql对传入和传出的数据的要求,便于传给前端和后端正确的数据。

:id, :name这些字段会对应之前创建的User模型中的字段。

这里定义了一个自定义的字段:books_count, Rails模型中并不存在,因此我们将其定义在字段列表的下方,

自定义的方法在models's  scope,可以通过self.books.size调用。

# app/graphql/types/user_type.rb
module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: true
    field :email, String, null: true
    field :books, [Types::BookType], null: true
    field :books_count, Integer, null: true

    def books_count
      books.size
    end
  end
end


# app/graphql/types/book_type.rb
module Types
  class BookType < Types::BaseObject
    field :title, String, null: false
  end
end

主查询类型

query_type.rb和mutation_type.rb这两种传入请求的路由,定义在模式schema里面,他们和Rails路由和资源有些相似。

# app/graphql/RAILS_APP_NAME_schema.rb
class GraphqlApiSchema < GraphQL::Schema
  mutation(Types::MutationType)
  query(Types::QueryType)
end

在query_type文件里面,定义:users和:user的字段,以及他们的方法,

users方法返回UserType类型的一组对象。

user方法接收一个类型为ID的:id的参数,返回一个UserType对象,(ID是一个内置的类型)

# app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject

    field :users, [Types::UserType], null: false

    def users
      User.all
    end

    field :user, Types::UserType, null: false do
      argument :id, ID, required: true
    end

    def user(id:)
      User.find(id)
    end
  end
end

查询字段

访问http://localhost:3000/graphiql, 在您的浏览器上粘贴users的查询代码,我们在上面添加了查询字段。在这里,我们确切地指定了我们希望API响应的内容; 在这种情况下,我们只需要一个用户名,电子邮件列表和他们拥有的书籍数量。

query {
  users {
    name
    email
    booksCount
  }
}

还可以查询单个用户相关信息:

query {
  user(id: 1) {
    name
    email
    books {
      title
    }
  }
}

Mutations(突变)

Mutations允许创建、修改、销毁数据,我们设置一个基类,用来扩展CreateUser的mutation。

# app/graphql/mutations/base_mutation.rb
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
end

Arguments参数:在这里我们指定接受哪些参数作为参数,以及它们是什么对象类型, 这是必需的。这有点类似于在Rails控制器中定义强参数,这里对进入的内容进行更细粒度的控制。

Fields字段:和上面查询字段概念相同,接收参数创建对象,同时希望返回一个user带有我们的新模型的字段,并附带一个errors数组。

Resolver解析器:resolve方法是执行ActiveRecord命令的地方。他返回一个带有和上面定义的字段名称一样的键的hash.

# app/graphql/mutations/create_user.rb
class Mutations::CreateUser < Mutations::BaseMutation
  argument :name, String, required: true
  argument :email, String, required: true

  field :user, Types::UserType, null: false
  field :errors, [String], null: false

  def resolve(name:, email:)
    user = User.new(name: name, email: email)
    if user.save
      # Successful creation, return the created object with no errors
      {
        user: user,
        errors: [],
      }
    else
      # Failed save, return the errors to the client
      {
        user: nil,
        errors: user.errors.full_messages
      }
    end
  end
end

最后,将新突变mutation添加到主突变类型类中,以便它暴露给我们的API。

# app/graphql/types/mutation_type.rb
module Types
  class MutationType < Types::BaseObject
    field :create_user, mutation: Mutations::CreateUser
  end
end

创建用户

测试,请打开http://localhost:3000/graphiql并粘贴以下查询。

注意我们传入一个createUser(input: {})对象; 这映射到:create_user接受单个input参数的字段。在graphql-ruby的文档中了解有关此设计的更多信息。

mutation {
  createUser(input: {
    name: "Matt Boldt",
    email: "me@mattboldt.com"
  }) {
    user {
      id
      name
      email
    }
    errors
  }
}

成功!我们刚刚通过GraphQL创建了第一个模型; 不需要额外的路由,控制器或序列化器。更重要的是,我们只从新创建的模型中准确返回了所需的数据。

 

来源:https://mattboldt.com/2019/01/07/rails-and-graphql/

 类似资料: