在 Rails 项目里使用 Graphql

简介: 在 Rails 项目里使用 Graphql

Rails 一直走在技术的前沿,从最早引入 scss、coffee 等就可以说明,Rails 一直在努力引入前端的前沿的技术。

过去的一年,前端涌现出各种框架,而 Rails 社区紧随其后有了相应的解决方案,比如 react-rails、react_on_rails 等等。

本文主要介绍了一下如何在 Rails 项目中使用 GraphQL,并抛出一些关于用 GraphQL 取代 API 的一些最佳实践,未必正确,权当抛砖引玉。欢迎拍砖。

关于GraphQL的介绍大家可以参看这里。这里主要介绍怎么集成到 Rails 项目中。

我们先创建一个 Rails 项目

$ rails new MyBlogApp
.
.
.
$ cd MyBlogApp

创建 Rails GraphQL API

首先,我们把rack-cors gem 添加到 Gemfile 中。

gem 'rack-cors', :require => 'rack/cors'

然后把以下内容添加至config/application.rb:

# config/application.rb
module MyBlogApp
  class Application < Rails::Application
    # ...
    config.middleware.insert_before 0, "Rack::Cors" do
      allow do
        origins '*'
        resource '*', :headers => :any, :methods => [:get, :post, :options]
      end
    end
  end
end

注意:在生产环境这样配置是相当危险的,具体配置请参考官方文档

然后我们生成一个 Blog model

$ rails generate model Blog title:string content:string author_id:integer

然后再添加一个 Author 的 model

rails generate model Author name:string

Blog 属于 Author:

# app/models/blog.rb
class Blog < ActiveRecord::Base
  belongs_to :author
end

Author 有许多 Blogs

# app/models/author.rb
class Author < ApplicationRecord
  has_many :blogs
end

然后,运行:

$ rails db:migrate

到这里为止,我们的 App 的基本内容已经告一段落了。 接下来,我们就开始创建 GraphQL API。

首先,在 Gemfile 里添加GraphQL gem:

gem 'GraphQL'

然后运行$ bundle install, 安装 gem。

我们创建 GraphQL API 的步骤是:

  1. 先创建 Type
  2. 再创建 Schema
  3. 创建 query

首先,我们在app目录下创建graph目录:

$ mkdir -p app/graph
$ mkdir -p app/graph/types

然后在config/application.rb文件里添加以下路径:

# config.application.rb
config.autoload_paths << Rails.root.join('app', 'graph')
config.autoload_paths << Rails.root.join('app', 'graph', 'types')

让我们先创建一个Query Type:

# app/graph/types/query_type.rb
QueryType = GraphQL::ObjectType.defile do
    name "Query"
    description 'The query root for this schema'
    field :blog do
       type BlogType
       argument :id, !types.ID
       resolve -> (obj, args, context) {
          Blog.find args[:id]
       }
    end
end

作为这次查询的根,我们定义了一个字段 blog,它会根据 id 返回一个 Blog 记录。

然后我们继续定义blog_typeauthor_type

# app/graph/types/blog_type.rb
BlogType = GraphQL::ObjectType.define do
   name 'Blog'
   description 'A Blog'
   field :title, types.String
   field :content, types.String
   field :author do
      type AuthorType
      resolve -> (obj, args, context) {
          obj.author
      }
   end
end
# app/graph/types/author_type.rb
AuthorType = GraphQL::ObjectType.define do
   name 'Author'
   description 'Author of Blogs'
   field :name, types.String
end

然后让我们在创建 GraphQL Schema:

# app/graph/blog_schema.rb
BlogSchema = GraphQL::Schema.define do
  query QueryType
end

现在剩下的就是将我们的 GraphQL Schema 的端点暴露出来,最好使用 POST 的方式: 别忘了修改此处:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # Use :null_session here
  protect_from_forgery with: :null_session
end

接下来,我们先生成一个QueriesController:

$ rails generate controller Queries create
# app/controllers/queries_controller.rb
class QueriesController < ApplicationController
  def create
    query_string = params[:query]
    query_variables = params[:variables] || {}
    result = BlogSchema.execute(query_string, variables: query_variables)
    render json: result
  end
end

在config/routes.rb 里添加以下路由:

resources :queries, via: [:post, :options]

That‘s it!

现在我们可以在终端下测试:

$ rails server

打开另一个终端:

$ rails console
> Blog.create title: "Intro to GraphQL", content: "Something something something. Blah blah blah. Etc etc etc."
> exit
$ curl -XPOST -d 'query={ blog(id: 1) { title content }}' http://localhost:3000/queries

返回:

{"data":{"blog":{"title":"Intro to GraphQL","content":"Something something something. Blah blah blah. Etc etc etc."}}}

后记:

几个思考:

  1. 与 ActiveRecord Serialiizer 相比,更灵活
  2. 验证:官方建议验证放在业务逻辑层,因此少量的 REST API 至少目前还是必不可少的。当然不差钱,可以用GraphQL::Pro
  3. 分页:我目前还没有测试,只是根据官方文档理解,无需再引入分页的 gem。因为 GraphQL 有相关的指令来进行分页。
  4. 异常处理:异常可以重写resolve方法,如: 定义个一个RescueFrom类放置到app/classes目录下面:
class RescueFrom
  def initialize error_superclass, resolve_func
    @error_superclass = error_superclass
    @resolve_func = resolve_func
  end
  def call obj, args, ctx
    @resolve_func.call obj, args, ctx
  rescue @error_superclass => err
    puts err.message
  end
end

然后重写blog_type.rb:

BlogType = GraphQL::ObjectType.define do
  name "Blog"
  description "A Blog"
  field :title, types.String
  field :content, types.String
  field :author do
    type AuthorType
    resolve RescueFrom.new(ActiveRecord::RecordNotFound,  -> (obj, args, ctx) {
      obj.author
    })
  end
end

但是,这种解决方法并不优雅,不能像在类似BaseController::ActionController这样的基类里使用rescue_from达到一劳永逸的效果。

总体来说,推荐指数,依然五星,希望大家尝试一下。

相关文章
|
3月前
|
Web App开发 存储 Ubuntu
如何为你的Rails应用配置Devise和OmniAuth
如何为你的Rails应用配置Devise和OmniAuth
40 4
|
3月前
|
数据管理 PHP 数据库
|
4月前
|
缓存 中间件 API
PHP框架详解 - Laravel 框架
PHP框架详解 - Laravel 框架
|
5月前
|
前端开发 测试技术 数据库
Ruby on Rails:快速开发Web应用的秘密
【6月更文挑战第9天】Ruby on Rails,一款基于Ruby的Web开发框架,以其高效、简洁和强大备受青睐。通过“约定优于配置”减少配置工作,内置丰富功能库加速开发,如路由、数据库访问。活跃的社区和海量资源提供支持,MVC架构与RESTful设计确保代码清晰可扩展。高效的数据库迁移和测试工具保证质量。Rails是快速构建Web应用的理想选择,未来将持续影响Web开发领域。
|
6月前
|
SQL 前端开发 API
前端需要学GraphQL 吗?
前端需要学GraphQL 吗?
58 2
|
Web App开发 前端开发 数据可视化
YApi和Swagger前后端分离开发
YApi和Swagger前后端分离开发
103 0
|
PHP 开发工具 数据库
laravel系列(二) Dcat admin框架开发工具使用
laravel系列(二) Dcat admin框架开发工具使用
274 0
|
JavaScript Go
搭建GraphQL服务
搭建GraphQL服务
77 0
|
JSON API 数据库
Sentry 开发者贡献指南 - Django Rest Framework(Serializers)
Sentry 开发者贡献指南 - Django Rest Framework(Serializers)
164 0