使用GraphQL结合Rails
上一次介绍了GraphQL的一些基本概念,这次我们将要结合GraphQL和Rails 做一个小的demo一个博客系统。先假设这个博客系统有如下模型:
Article
class Article < ActiveRecord::Base
belongs_to :user
has_many :comments
end
User
class User < ActiveRecord::Base
has_many :articles
has_many :comments
end
Comment
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :article
end
好,然后要在Gemfile中添加下面这几个用于解析GraphQL查询语法的gem和一个用于配合rails使用的GraphQL查询工具。
gem 'graphql'
gem 'graphiql-rails'
gem 'graphql-libgraphqlparser', '~> 0.2'
然后 bundle install
安装
接下来需要我们在app目录下创建graph及其子目录用于存放GraphQL的定义类型等信息
├── app
│ ├── graph
│ │ ├── fields
│ │ ├── schemas
| | ├── mutations
│ │ └── types
- fields 用于存放程序中自定义的GraphQL::Field信息。
- schemas 用于存放定义的。
- types 中存放所有自定义的业务类型,和查询类型。
- mutations 中用于存放修改数据的具体实现逻辑。
查询
好,接下来就应该是为系统中的三个模型,建立GraphQL对应的类型:
# UserType
UserType = GraphQL::ObjectType.define do
name 'User'
description 'user type'
field :id, !types.ID
field :name, !types.String
field :email, !types.String
field :articles, -> { types[!ArticleType] }
end
# ArticleType
ArticleType = GraphQL::ObjectType.define do
name 'Article'
description 'article type'
field :id, !types.ID
field :title, !types.String
field :content, !types.String
field :comments, -> { types[!CommentType] }
field :user, UserType
end
# CommentType
CommentType = GraphQL::ObjectType.define do
name 'Comment'
description 'Comment type'
field :id, !types.ID
field :user_id, !types.ID
field :content, !types.String
field :article, ArticleType
field :user, UserType
end
在以上的模型类型定义中有个问题需要注意到,就是在 ArticleType 中定义了一个字段是CommentType的集合,这里应用了CommentType这个常量,然后CommentType这个类型定义中又引用了ArticleType这个常量所以会产生 循环加载的情况。,解决这个问题的办法就是上面的代码所写到的,在可能出现循环加载的地方使用lambda表达式 将类型包装起来,这样GraphQL解析器就只会在实际调用到这个字段的时候才会加载这个类型。
上面是定义了模型的类型,接下来我们就要来定义,在查询中最重要的查询类型的定义:
QueryType = GraphQL::ObjectType.define do
name 'Query'
description "query root for this schemas"
field :user do
type UserType
argument :id, types.ID
resolve -> (obj, args, ctx) do
User.find_by(id: args['id'])
end
end
field :articles do
type !types[!ArticleType]
resolve -> (obj, args, ctx) do
Article.all
end
end
field :article do
type ArticleType
argument :id, types.ID
resolve -> (obj, args, ctx) do
Article.find_by(id: args['id'])
end
end
end
查询类型,作为在GraphQL应用中一个schema所唯一包含的查询,定义了一个schema中所以能够接收到的查询字段和类型,其中这个查询类型还需要定义如何去从数据库查询这些具体类型的数据。例如上面的代码中定义的article字段类型中就使用GraphQL定义的resolve方法,用于查询给定ID下的article记录。
下面我们就来编写应用程序的入口,控制器和其对应的GraphQL,schema
首先是路由的定义:
** app/graph/schemas/query_schema.rb**
QuerySchema = GraphQL::Schema.new(query: QueryType)
** ap/controllers/queries_controller.rb **
class QueriesController < ApplicationController
def create
result = ::QuerySchema.execute(params[:query])
render json: result
end
end
接下来是路由的定义:
mount GraphiQL::Rails::Engine, at: "/graphql", graphql_path: "queries"
root to: redirect("/graphql")
resources :queries
上面路由定义中的第一行是挂在,Rails的GraphQL可视查询工具的访问路由。
根据上面的这些代码,我们的基于GraphQL实现的博客查询系统就基本可以使用了,接下来就是看看查询的效果了。
通过上图左面的查询字符串,我们就可以得到到右面的结果,用户ID为15的用户名和他所发文章的标题。
小结
本文中介绍了,GraphQL结合Rails实现的博客的查询API,可以从中体现出,在GraphQL中的简单类型定义下,我们就可以灵活的实现,按需API查询功能。这个系列的下一篇文章将会写到,这次提到但是没写的,如何使用GraphQL去修改服务器的的数据。