前言
什么是GraphQL?
简而言之就是用于API的查询语言。你可以理解为只需要一次请求加上点查询参数,就可以定制任何后端有的信息。
在继续讲GraphQL之前我们先来看看我们常用的RESTFul接口(REST和RESTFul的关系就像Beatuy和Beautiful的关系)的优缺点(没缺点我们换它干啥)
RESTFUL接口的优缺点
RESTFul接口在历史上最大的贡献在于实现了前后端分离以及后端的无状态设计(后端无状态设计就是后端的资源不需要建立session,完全依赖客户端的请求)。
优点:
- 不仅有http状态码可供错误机制处理还可以方便的自定义错误码
- 实现了http缓存,关于Http缓存可以参考这篇文章
缺点 :
- 接口冗长。(随着业务的不断丰富,单个RESTFul接口可能变得越来越冗长)
- 文档难于维护。(RESTFul文档的维护缺失是个很耗人力的事)
- 无法自定义相应结果。(有时候我只需要一个用户名,接口RESTFul给我返回了一大堆不需要的字段)
- 效率低下。(如果用RESTFul接口的话,经常会遇到需要几个RESTFul接口才能完成的展示页面的情况)
针对RESTFul的缺点,GraphQL就应运而生了。来看下它的优缺点:
优点
- 自定义查询API,体验好。(后端性能是否提高暂且不论,但是前后端代码减少了不少,一个接口搞定所有需要的信息)
- 文档自动生成。(再也不需要人工维护文档了,可以往下看,有例子)
- 可以自定义查询结果。(接口调用者决定需要哪些字段,后端会根据前端的查询参数返回定制的字段)
缺点
- 无http状态码。(无论查询条件是否正确,http状态码返回的都是200,而且自定义的状态码不好处理)
- 无缓存(据说Apollo GraphQL这个项目已经集成了,具体插件是apollo-cache-inmemory);
以上大致讲了下GraphQL的一个优缺点,自然也能大致了解其应用场景,在GraphQL还没有完全解决它的缺点之前,我们可以将其和RESTFul接口搭配使用。接下来我们基于koa 2.5.1 和 apollo-server-koa 2.4.8 进行实战演练
由于我这个项目是之前基于RESTFul接口的,这里我将GraphQL集成到上面(也就是在后端返回和前端之间加入GraphQL这层拦截层)。
先看下项目结构
总共基于 koa 和 apollo-server-koa 操作有一下6个步骤
- 引入插件
apollo-sever-koa
- 创建apollo server并传入GraphQL表
- 创建koa对象
- 将koa对象作为中间件传入apollo server
- 监听端口
- 定义GraphQL表并传入query对象和Mutation对象
【细节】
安装插件 apollo-sever-koa
和 koa
cnpm install apollo-server-koa koa
创建app.js
const Koa = require('koa');
const {ApolloServer, gql} = require('apollo-server-koa'); // graphql-koa插件
const schema = require('./server/graphql/index.js'); //自定义的GraphQL的表
const server = new ApolloServer({ //创建Graphql server
schema,
context: ({ ctx }) => {
// let token = ctx.
}
});
server.applyMiddleware({app}); //apollo server使用koa中间件
app.listen(9527, ()=> { //监听端口
console.log(`server running success at ${server.graphqlPath}`)
})
创建GraphQL表
//index.js
const { articleList } = require('./schemas/articles');
const { postLogin } = require('./schemas/user');
const {
GraphQLSchema,
GraphQLObjectType
} = require('graphql');
//总查询对象
let queryObj = new GraphQLObjectType({
name: 'query',
fields: () => ({
articleList: articleList
})
})
//总体变更对象
let mutationObj = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
postLogin: postLogin
})
})
//GraphQL总表
let schema = new GraphQLSchema({
query: queryObj,
mutation: mutationObj
})
module.exports = schema
创建文章表
const {
GraphQLObjectType, //对象类型
GraphQLString, //字符串类型
GraphQLList, //数组类型
GraphQLInt //int类型
} = require('graphql');
//文章业务逻辑控制
const ArticleController = require('../../controller/article.js');
//定义评论对象
let commentType = new GraphQLObjectType({
name: 'commentType',
description:'评论对象',
fields() {
return {
name: { type: GraphQLString },
email: { type: GraphQLString },
comment_content: { type: GraphQLString }
}
}
})
//定义单个文章对象
let ArticlesType = new GraphQLObjectType({
name: 'single_article_type',
description: '单个文章对象', // 这里写详细点有助于自动生成文档,减少前后端沟通成本
fields() {
return {
_id: { type:GraphQLString },
page_view_time: { type: new GraphQLList(GraphQLString) },
user_view: { type: new GraphQLList(GraphQLString)},
comment: { type: new GraphQLList(commentType) },
page_view_count: { type: GraphQLInt },
md_content: { type: GraphQLString },
html_content: { type: GraphQLString },
update_time: { type: GraphQLString },
create_time: { type: GraphQLString },
title: { type: GraphQLString },
user_view_count: { type: GraphQLInt },
comment_count: { type: GraphQLInt }
}
}
})
//定义文章列表对象
let articleList = {
name: 'query articles list',
type: new GraphQLList(ArticlesType),
args: { //定义参数
id: {
name: 'id',
type: GraphQLString
},
limit: {
name: 'limit',
type: GraphQLInt,
},
skip: {
name: "skip",
type: GraphQLInt
}
},
async resolve(root, params, options) {
let result = await ArticleController.queryArticle({
id: params.id,
limit: params.limit,
skip: params.skip
});
return result;
}
}
module.exports = {
articleList
}
创建用户表
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLInt
} = require('graphql');
//用户逻辑控制
const UserController = require('../../controller/user.js');
const UserOutputType = new GraphQLObjectType({
name: 'user_operage_type',
fields() {
return {
name: { type: GraphQLString },
token: { type: GraphQLString }
}
}
})
const postLogin = {
description: 'postlogin',
type: UserOutputType,
args: {
username: {
name: 'username',
type: GraphQLString
},
password: {
name: 'password',
type: GraphQLString
}
},
async resolve(root, params, options) {
let user = await UserController.postLogin({
username: params.username,
password: params.password
});
return user;
}
}
module.exports = {
postLogin
}
看效果
点击schema
就可以查看所有字段,自动生成API文档!
网上很多express + graphql的例子但是基于koa 2.x和 apollo-server-koa 2.x的版本的例子比较少,这里我也是查阅了很多资料和官网实践整理出来的,若有意见或疑问欢迎留言。
参考文献
https://blog.csdn.net/jeffrey20170812/article/details/83348549
//www.greatytc.com/p/2ad286397f7a?open_source=weibo_search
http://web.jobbole.com/94006/
apollo graphql官网