它是为了解决什么问题而产生的
当我们的程序变得日益庞大、臃肿,各个业务之间的数据、前端界面之间的耦合度就可能会很高,这样不仅维护成本和开发成本会飙升,而且还可能做的更多重复性的工作。由此我们想到可以将通用的一些东西抽取出来。
针对此问题前端领域衍生出了很多优秀的框架,其中当以react和vue应用最广泛,通过组件化的方式实现代码的重用和相互独立。
而graphql则是针对后端的,前端来决定请求的数据格式,后端返回固定的数据模型,就好像前端发送了sql的查询语句,这样不仅可以解决程序中各个不同业务之间的数据依赖,还可以达到数据的重用。
吧啦吧啦结束了,是时候展现真正的技术了。
实现一个基于graphql的服务
schema
schema应该算是整个graphql的核心内容。它用来描述服务器返回的额数据类型以及结构,而在客户端请求的时候要保证请求的内容与服务器要求的格式一致,举个栗子。
当我们的请求体是这个样子的
{
article(id: "1") {
title
}
}
那么返回的数据结构就是
{
"data": {
"article": {
"title": "graphql初探"
}
}
}
这里有必要说明一下,GraphQL 的一个查询请求被称为一份 query 文档(query document),即 GraphQL 服务能够解析验证并执行的一串请求字符串。
...?query={article(id:"1"){title}}
我们来生成一个稍微复杂一点的schema
import { GraphQLObjectType, GraphQLSchema, GraphQLList, GraphQLString } from 'graphql'
import { getArticles } from '../model/article'
const articleType = new GraphQLObjectType({
name: 'Article',
fields: {
ctime: { type: GraphQLString },
title: { type: GraphQLString },
}
})
export const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
articleList: {
type: new GraphQLList(articleType),
resolve: async (_, args) => {
return await getArticles()
}
}
}
})
})
这段代码创建了一个schema实例,它返回了一个可能包含了多个对象的数组,在对象定义了title和ctime字段。返回的数据要指定类型,在graphql中定义了多种类型:
- GraphQLInt: 整数,对应 JavaScript 的 Number
- GraphQLFloat: 浮点数,对应 JavaScript 的 Number
- GraphQLString: 字符串,对应 JavaScript 的 String
- GraphQLBoolean: 布尔值,对应 JavaScript 的 Boolean
- GraphQLID: ID 值,是一个序列化后值唯一的字符串,可以视作对应 ES 2015 新增的 Symbol
- GraphQLList: 数组
- GraphQLObjectType: 对象
- GraphQLNonNull: 强制类型的值不能为 null,并且在请求出错时一定会报错。可以用于必须保证值不能为 null 的字段
- GraphQLUnionType: 联合类型用于描述某个字段能够支持的所有返回类型,即可能返回的类型有多重的情况下。
const PetType = new GraphQLUnionType({
name: 'Pet',
types: [Type1, Type2],
resolveType(value) {
if (value instanceof A) {
return Type1;
}
if (value instanceof B) {
return Type2
}
}
})
由于GraphQL并不是一个面向图数据库的查询语言,而是一个数据抽象层,包括数据格式、数据关联、查询方式定义与实现等等一揽子的东西。所以我们最后要调用一个查询数据库的服务来获取真正的数据,也即是下面这段代码
resolve: async (_, args) => {
return await getArticles()
}
连接schema
很明显我们需要将schema 和服务器连接起来。
//server.js
import express from 'express'
import { schema } from './schema'
import graphqlHTTP from 'express-graphql'
import bodyParser from 'body-parser
let app = express()
let PORT = 3000
//Parse post content as text
app.use(bodyParser.text({ type: 'application/graphql' }))
app.get('/articles', graphqlHTTP({schema}))
let server = app.listen(PORT, function() {
let host = server.address().address
let port = server.address().port
console.log('GraphQL listening at http://%s:%s', host, port)
})
在这里推荐使用express-graphql它会优先解析我们的body或query里的参数,如果都没有的话,则会解析header里的参数。
可以通过访问这个地址来验证
http://localhost:3000/articles?query={articleList{ctime,title}}
mutation
另外除了查询操作外我们还有常用的update操作,针对这样的操作很明显查询是满足不了得,所以就要涉及到变异(mutation,即修改数据)
let schema = new GraphQLSchema({
query: ...
mutation: new GraphQLObjectType({
name: 'RootMutationType',
fields: {
updateCount: {
type: GraphQLInt,
description: 'Update the count',
resolve: function() {
count += 1
return count
}
}
}
})
})
至此我们已经完成了一个graphql的服务,但是在实际的项目中涉及到的肯定是不止这些,据说在react项目中relay和graphql更配哦。
在 relay + graphql 的模型中,每个组件指定自己需要的数据。Relay 调用数据,数据更新时,提供给组件最新的数据,并在客户端做缓存。app 需要更新数据时,在 Action 中创建一个 graphql 变更,这和 Redux 类似。下次会尝试relay+graphql。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。