尝试着使用 Vue×Apollo×Rails 学习 GraphQL
首先
团队生活 x cyma Advent Calendar 2018 的第25天,由株式会社Ateam Lifestyle 的工程师 @sakupa80 负责。
最近在HeadlessCMS等地方经常听说GraphQL的话题,所以我决定边做教程边体验一下。
作为后端,我选择了平时用来开发的Rails,而在前端方面我选择了起步简单的Vue.js。
按照这篇文章的顺序进行,可以学习到GraphQL的基础知识,希望对那些想要尝试GraphQL的人有所帮助。
开发环境
-
- macOS High Sierra
-
- Ruby 2.5.1
-
- Rails 5.2.2
-
- graphql-ruby 1.8.11
-
- Vue 2.5.2
- MySQL 5.7.21
由于graphql-ruby版本为1.8,因此我们将以基于类的方式进行描述。
环境构建
首先,我们将从Rails开始进行构建。
创建一个名为graphql_rails的目录。
$ mkdir graphql_rails ; cd $_
$ bundle init
敲击后生成了Gemfile。
稍作编辑。
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.5.1'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.2'
接下来执行bundle install。
$ bundle install --path vendor/bundler
安装完成后,请执行”rails new”命令。
由于我们要使用Vue.js作为前端,请在选项中指定Vue。
$ bundle exec rails new . --webpack=vue
一旦完成后,接下来会添加用于GraphQL的gem等内容。
gem 'mysql2'
gem 'graphql'
gem 'graphiql-rails'
修改后再次执行bundle install。
$ bundle install
由于graphql-ruby的引入,现在可以轻松地通过Rails的generate命令创建graphql环境,请执行以下命令。
$ bundle exec rails g graphql:install
然后,在/app目录下生成了一个新的文件夹。
.
├── graphql_rails_schema.rb
├── mutations
└── types
├── base_enum.rb
├── base_input_object.rb
├── base_interface.rb
├── base_object.rb
├── base_scalar.rb
├── base_union.rb
├── mutation_type.rb
└── query_type.rb
因为我们要使用MySQL数据库,所以先启动MySQL并编辑database.yml文件。
$ mysql.server start
default: &default
adapter: mysql2
encoding: utf8
database: rails_development
username: root
password:
由于本次假设使用GraphQL进行简单的文章数据传输,
我们需要生成一个名为Post的模型。
只需要有标题和内容两列就可以了。
我们还要提前生成一个作为前端端点的HomeContollor。
$ bundle exec rails g controller Home index
$ bundle exec rails g model Post title:string content:text
$ bundle exec rails db:create
$ bundle exec rails db:migrate
我会生成用于GraphQL的对象。
$ bundle exec rails g graphql:object Post id:ID! title:String! content:String!
我们已经准备好了!现在可以尝试启动服务器并通过浏览器进行确认。
$ bundle exec rails s -p 5000
在graphql-ruby中,它为我们提供了一个可以直接生成和执行的测试客户端。
当我们检查routes.rb文件时,我们可以确认已经添加了可以访问测试客户端的路由。
Rails.application.routes.draw do
if Rails.env.development?
mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
end
post "/graphql", to: "graphql#execute"
...
end
让我们尝试访问http://localhost:5000/graphiql。
![スクリーンショット 2018-12-21 10.54.30.png](https://cdn.silicloud.com/blog-img/blog/img/657d8042913a08637a6a95b6/35-0.png)
让我们来实际实现GraphQL。
询问和变异
让我们在测试客户端上实际发送请求试试看。
在GraphQL中,如果是进行数据的“读取”操作,可以使用query。而如果是进行数据的“创建”、“更新”、“删除”等操作,则可以使用mutation。
使用查询获取所有文章。
首先,创建一个字段来访问GraphQL中的Post对象。
module Types
class PostType < Types::BaseObject
field :id, ID, null: false
field :title, String, null: false
field :content, String, null: false
end
end
您需要写出您想要访问PostType对象的列名,如上所述。
接下来,让我们在query_type.rb中添加一个可以读取文章数据的字段。
module Types
class QueryType < Types::BaseObject
field :all_posts, [PostType], null: false
def all_posts()
Post.all
end
end
end
你只是做了一个空的参数引用,然后只是用Post.all而已。
因此,如果没有CSRF令牌,就会被责备,所以这次我们特别设置为跳过令牌检查。
class GraphqlController < ApplicationController
skip_before_action :verify_authenticity_token
...
接下来,为了确认,我们先在rails控制台等地创建虚拟数据。
创建了虚拟数据后,我们将在测试客户端中传递下面的查询语句来进行测试。
{
allPosts {
title
}
}
![スクリーンショット 2018-12-21 11.51.45.png](https://cdn.silicloud.com/blog-img/blog/img/657d8042913a08637a6a95b6/51-0.png)
你已经成功地获取了所有数据!我也确认只返回了指定的标题!
使用突变来创建文章。
接下来,让我们使能够创建文章。
首先,我们要在MutationType中添加一个用于创建文章的字段。
module Types
class MutationType < Types::BaseObject
field :create_post, mutation: Mutations::CreatePost
end
end
接下来,我们来实现CreatePost类。
module Mutations
class CreatePost < GraphQL::Schema::Mutation
null false
argument :title, String, required: true
argument :content, String, required: true
field :post, Types::PostType, null: false
def resolve(**arg)
post = Post.new(
title: arg[:title],
content: arg[:content]
)
if post.save
{
post: post
}
else
raise GraphQL::ExecutionError, post.errors.full_messages.join(", ")
end
end
end
end
同样需要首先指定字段,就像Types、Query一样。
然后实际处理是在resolve方法中进行的。
这是一个简单的操作,只是保存传入的标题和内容。
让我们也在测试客户端上运行一下。尝试传递以下查询。
mutation {
createPost(title: "テスト2", content: "あああ")
{
post {
id
title
}
}
}
![スクリーンショット 2018-12-21 12.19.55.png](https://cdn.silicloud.com/blog-img/blog/img/657d8042913a08637a6a95b6/61-0.png)
我也确认了文章的创建!数据也被正确返回了。
那么,现在我们一起来创建实际的前端协作部分吧。
前端实现
最後,我们将进行前端实现。使用之前创建的查询来获取所有文章,然后制作文章列表页面。
在Vue中,我们使用一个名为Apollo Client的模块来实现与GraphQL的集成。我们需要安装所需的模块。在下面的示例中,我们使用yarn,但应该也可以使用npm。
$ yarn add vue-apollo vue-router apollo-client graphql graphql-tag apollo-boost
当安装完成后,我们将修正视图。
<div id="app">
<div>
<router-view></router-view>
</div>
</div>
<%= javascript_pack_tag 'app' %>
在Rails中只需这样就可以了。
接下来我们开始编写Vue。
首先,我们要创建一个名为app.js的Vue对象。
import Vue from 'vue/dist/vue.esm.js'
import VueRouter from 'vue-router'
import VueApollo from 'vue-apollo'
import ApolloClient from "apollo-boost"
import Index from './components/index.vue'
const apolloClient = new ApolloClient({
uri: "http://localhost:5000/graphql"
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})
Vue.use(VueApollo)
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Index },
],
})
const app = new Vue({
router,
apolloProvider,
el: '#app',
})
接下来,我们将创建用于实际处理的组件。
<template>
<div>
<ul v-for="post in allPosts">
<li>タイトル: {{post.title}}</li>
<li>内容: {{post.content}}</li>
</ul>
</div>
</template>
<script>
import gql from 'graphql-tag'
const POSTS_QUERY = gql`
query allPosts{
allPosts {
title
content
}
}`
export default {
name: 'index',
data () {
return {
allPosts: []
}
},
apollo: {
allPosts: {
query:POSTS_QUERY
}
}
}
</script>
只需要一种选择:使用graphql-tag来解析查询并将其传递给GraphQL。在前端中,只是通过for循环将文章进行展示。
最后将home#index设置为根页面。
Rails.application.routes.draw do
root to: 'home#index'
让我们来确认一下是否显示了。
![スクリーンショット 2018-12-23 4.51.26.png](https://cdn.silicloud.com/blog-img/blog/img/657d8042913a08637a6a95b6/77-0.png)
如果已经完成到这一步,那就算完成了。
因为我已经在mutation中添加了文章创建功能,所以如果在前端实现了向mutation发送查询,也可以执行该功能。
总结
通过使用GraphQL和Rails,可以利用类型系统。与REST相比,可以只获取所需的数据,使客户端切换更容易。我们了解到了这些强大的优势。但是,GraphQL似乎没有eager loading等机制,可能会出现N+1问题等缺点。我会进一步调查与服务器端合作使用哪种语言更容易。
结束时
@ihsiek和Ateam Lifestyle x cyma的Advent Calendar运营成员们,
非常感谢你们!
希望您能阅读Ateam Lifestyle x cyma Advent Calendar 2018,因为我们还有其他出色的成员为您撰写文章。
在A集团,我们正在招募具有挑战精神和活力的伙伴一起工作。如果您对此感兴趣,请务必查看A集团的招聘网站:https://www.a-tm.co.jp/recruit/
请问我能参考你的论文吗?
May I refer to your paper?
https://qiita.com/vsanna/items/031aa5a17a2f284eb65d : 这里有一个关于Vue Apollo和GitHub的文章,链接为https://qiita.com/vsanna/items/031aa5a17a2f284eb65d。
https://qiita.com/naoki85/items/51a8b0f2cbf949d08b11 : 这篇文章介绍了关于Vue Apollo和GitHub的内容,链接为https://qiita.com/naoki85/items/51a8b0f2cbf949d08b11。
https://qiita.com/193/items/bf9842af0e1df8fb7226 : 这篇文章详细介绍了Vue Apollo和GitHub,链接为https://qiita.com/193/items/bf9842af0e1df8fb7226。
https://marketing-web.hatenablog.com/entry/vue-apollo_github : 在这篇博客中有关于Vue Apollo和GitHub的内容,链接为https://marketing-web.hatenablog.com/entry/vue-apollo_github。