美文网首页
graphql in rails

graphql in rails

作者: EvanCui | 来源:发表于2019-02-28 16:23 被阅读0次

GraphQL是个新的API标准,它比REST标准更高效,更强大且更灵活。它由Facebook开源,目前应用已经很广泛。

现在大部分程序都需要从服务端获取数据,而这些数据大部分都存在数据库中,所以API的作用就是为这些程序提供合适的接口。 不要误认为Graphql是一门数据库相关的技术,事实上它只是为API而存在的.
而在ruby的生态系统中,Github和Shopify等公司已经将GraphQL应用在了生产环境中。

在rails中使用添加gem即可:

gem 'graphql'
gem 'graphiql-rails'

使用rails generate graphql:install
将生成app/graphql/...的默认目录。这样将在我们的系统中加载一个GraphQL Server和一个GraphQL Client.(默认可通过http://localhost:3000/graphiql访问)

我们实现一个例子:

假定数据结构:

  posts:
     title:stirng
     content:text
数据查询:

通过id查询post

# app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseObject
    description "query post"
    field :id, ID, null: false
    field :title, String, null: false
    field :content, String, null: false
  end
end

查询posts列表:

# app/graphql/types/query_type.rb
field :posts, [Types::PostType], null: false
def posts(options={})
    Post.all.order("created_at DESC").
end

基于上面的例子,我们介绍下field方法的用法:
在GraphQL中,对象通过field方法,将对象的数据或者关联的数据暴露出去。

field :name, String, "The unique name", null: false

field第二个参数表示返回值,返回值类型为GraphQL内置类型,包括Integer, Float, ID, Boolean或者数组如[String],还可以是一个GraphQL的对象类型如关联对象PostType

null关键词指定返回值是否可以为空。

null: true, 意味着该field可为空
null: false,意味着不可为空,如果程序在该位置返回了一个空值,前端请求时将会报错。

下面是官方文档的几个示例,写的比较清晰:

field :name, String, null: true # `String`, may return a `String` or `nil`
field :id, ID, null: false # `ID!`, always returns an `ID`, never `nil`
field :teammates, [Types::User], null: false # `[User!]!`, always returns a list containing `User`s
field :scores, [Integer, null: true], null: true # `[Int]`, may return a list or `nil`, the list may contain a mix of `Integer`s and `nil`s

上面我们定义了类型,GraphQL server还不知道怎么操作该type,所以我们需要写一个resolver函数,
resolver是GraphQL中用来执行数据查询的函数,每一个field都需要对应一个resolver函数。一旦数据查询开始,服务器将调用这些resolver将其对应于指定的field.
所以我们在query_type.rb中添加:

# app/graphql/types/query_type.rb
field :post, PostType, null: true do
  description "Find a post by id"
  argument :id, ID, required: true
end

def post(id:)
  Post.find(id)
end

同理field表示返回的对象记录,其中argument表示需要传入的参数,其参数意味着,参数名,参数类型,是否必须存在。
完成上面的代码,你将可以通过http://localhost:3000/graphiql浏览器工具进行查询。
查询代码为:

# 单条post查询   
query{
  race(id: 1) {
    id
    title
    content
  }
}

# post列表查询
query{
  races {
    id
    title
    content
  }
}
数据修改:

mutation的声明类似query的声明,graphql目录下默认生成了一个mutations目录和一个mutation_type.rb文件,该文件即为mutation的根类型.

# app/graphql/mutations/create_post.rb
module Mutations
  class CreatePost < Mutations::BaseMutation

    graphql_name 'CreatePost'
    # define return fields
    field :post, Types::PostType, null: false
    field :success, Boolean, null: true

    # define arguments
    argument :title, String, required: true
    argument :content, String, required: true

    # define resolve method
    def resolve(**args)
      post = Post.new(title: args[:title], content: args[:content])
      post.save
      # 注: 下面{}内的key必须与上面field指定的参数一致.
      {
        post: post,
        success: post.errors.blank?
      }
    end
  end
end

将mutation暴露给mutation类型:

module Types
  class MutationType < BaseObject
    field :createPost, mutation: Mutations::CreatePost
  end
end

完成上面的代码,你将可以通过http://localhost:3000/graphiql浏览器工具进行操作。

mutation {
  createPost(
    input:{
      title: "this is a test title"
      content: "this is a test content"
    }
  ) {
    post
    success
  }
}

Graphql中mutation是一些特殊的fields,它的作用是完成程序中更改操作。
比如数据的增删改,数量增加,对文件的增删改以及清除缓存等操作。
mutation的field方法通过接收inputs和调用arguments,然后通过fields返回值。

context的使用
通过上面的mutation我们可以在系统中创建用户实现注册,登录等,但是如何获取current_user呢?

app/controllers/graphql_controller.rb:
def execute
    ...
    context = {
      current_user: current_user
    }
    ...
  rescue => e
    raise e unless Rails.env.development?
    handle_error_in_development e
  end
private
def current_user
    access_token = request.headers["HTTP_X_ACCESS_TOKEN"]
    User.find_by(access_token: access_token)
rescue ActiveRecord::RecordNotFound
    nil
end

这样我们就可以在resolver函数中通过context[:current_user]来回去当前用户了。
GraphQL还提供了很多方便的功能,如程序错误处理, Timeout处理等等,使用起来非常方便,后期遇到好的用法,再更新。

实现对current_user的验证
由于graphql所有到请求状态都是200,所有的error信息都放在errors字段里,所以对用户权限的验证就不能走http的401状态了,作者具体解释为:


image.png

可参考写法如下:

raise GraphQL::ExecutionError, :forbidden unless context[:current_user]

相关文章

网友评论

      本文标题:graphql in rails

      本文链接:https://www.haomeiwen.com/subject/yrxkuqtx.html