一、什么是GraphQL
GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑,GraphQL 可以运行在任何后端框架或者编程语言之上。
GraphQL 全称叫 Graph Query Language,官方宣传语是“为你的 API 量身定制的查询语言”。
用传统的方式来解释就是:相当于将你所有后端 API 组成的集合看成一个数据库,用户终端发送一个查询语句,你的 GraphQL 服务解析这条语句并通过一系列规则从你的“ API 数据库”里面将查询的数据结果返回给终端,而 GraphQL 就相当于这个系统的一个查询语言。
二、存在的问题:
REST API :服务端决定有哪些数据返回,客户端只能挑选使用,如果数据过于冗余也只能默默接收再对数据进行处理;而数据不能满足需求则需要请求更多的接口。以上就是我们常说的“过渡获取”和“欠缺获取”
由于"过度"和"欠缺"的获取问题及其对客户端应用程序性能的影响,促进有效获取的 API 技术才有机会在市场上引起轰动 —— GraphQL 大胆地介入并填补了这一空白。
举个简单的例子
graphQL 查询语句
{
getCategories {
id
name
products {
id
name
}
}
}
输出结果
{
"data": {
"getCategories": [
{
"id": "1",
"name": "吃的",
"products": [
{
"id": "1",
"name": "浪味仙"
}
]
},
{
"id": "2",
"name": "喝的",
"products": [
{
"id": "4",
"name": "茶颜悦色"
}
]
}
]
}
}
客户端现在不想要商品信息了 只需要获取商品分类,你只需要将你的查询语句修改一下即可
{
getCategories {
id
name
}
}
输出结果
{
"data": {
"getCategories": [
{
"id": "1",
"name": "吃的"
},
{
"id": "2",
"name": "喝的"
}
]
}
}
现在我想要通过这一个查询,获取到商品分类,以及所有商品的信息
{
getCategories {
id
name
}
getProducts {
id
name
}
}
输出结果
{
"data": {
"getCategories": [
{
"id": "1",
"name": "吃的"
},
{
"id": "2",
"name": "喝的"
}
],
"getProducts": [
{
"id": "1",
"name": "浪味仙"
},
{
"id": "4",
"name": "茶颜悦色"
}
]
}
}
是不是很方便,通过上面例子,可以发现GraphQL API有如下优点:客户端可以自定义查询语句,数据获取灵活多变,服务端按需返回数据,减少网络的开销,提高了性能。而这些都是Restful API的弊端。
三、基础概念
在介绍GraphQL的使用之前先要了解一些概念
1.GraphQL 类型系统(Type System)
类型系统 是整个 GraphQL 的核心,它用来定义每个查询对象和返回对象的类型。GraphQL的Type简单可以分为两种,一种叫做Scalar Type(标量类型),另一种叫做Object Type(对象类型)。
a)标量类型(Scalar Types)
一个对象类型有自己的名字和字段,而某些时候,这些字段必然会解析到具体数据。这就是标量类型的来源:它们表示对应 GraphQL 查询的叶子节点。
标量类型这个概念有点重要,这个字段是不是一个标量类型,决定了我们这个查询是不是还要继续往更深层去递归查询。
GraphQL 自带一组默认标量类型:
GraphQLInt:有符号 32 位整数。
GraphQLFloat:有符号双精度浮点值。
GraphQLString:UTF‐8 字符序列。
GraphQLBoolean 或者 false。
GraphQLID:ID 标量类型表示一个唯一标识符。
b)对象类型
一个 GraphQL schema 中的最基本的组件是对象类型,表示你可以从服务上获取到什么类型的对象,以及这个对象有什么字段。
例如 以下这段代码中 id 、name 就是标量类型。products就是一个对象类型
type categories {
id
name
products {
id
name
}
b)Schema
在 GraphQL 中,类型的定义以及查询本身都是通过 Schema 去定义的。
Schema它是用来描述接口获取数据逻辑的。我们可以将Schema理解为多个Query组成的一张表。
这里又涉及一个新的概念Query,GraphQL中使用Query来抽象数据的查询逻辑,当前标准下,有三种查询类型,分别是query(查询)、mutation(更改)和subscription(订阅)。
query(查询):当获取数据时,应当选取Query类型
mutation(更改):当尝试修改数据时,应当使用mutation类型
subscription(订阅):当希望数据更改时,可以进行消息推送,使用subscription类型
c)Resolver
我们已经了解了graphQL的类型系统和schema,那么我们的数据到底怎么来呢?答案是来自 Resolver 函数。
Resolver 的概念非常简单。Resolver 对应着 Schema 上的字段,当请求体查询某个字段时,对应的 Resolver 函数会被执行,由 Resolver 函数负责到数据库中取得数据并返回,最终将请求体中指定的字段返回。
type Movie {
name
genre
}
type Query {
movie: Movie!
}
当请求体查询movie时,同名的 Resolver 必须返回Movie类型的数据。当然你还可以单独为name字段使用独立的 Resolver 进行解析。
以上是一些最基本的概念,最后我们来详细的分析一下,graphQL是如何定义并查询到数据的
服务端的schema:
//schema.js
let categories = [
{ id: '1', name: '吃的' },
{ id: '2', name: '喝的' },
]
let products = [
{ id: '1', name: '浪味仙', category: '1' },
{ id: '4', name: '茶颜悦色', category: '2' },
]
//分类里定义每个字段
const Category = new GraphQLObjectType({
name: 'category',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
products: {
type: new GraphQLList(Product),//每个分类下是一个数组,而不是一个对象
resolve(parent) {//parent代表上一层的查询结果
return products.filter(item => item.category === parent.id)
}
}
})
})
//产品里定义每个字段
const Product = new GraphQLObjectType({
name: 'product',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString }
})
})
//查询接口
const RootQuery = new GraphQLObjectType({
name: 'root',
fields: {
getCategories: {
type: new GraphQLList(Category),
args: {
},
resolve(parent, args) {
return categories
}
}
}
})
module.exports = new GraphQLSchema({
query: RootQuery,//查询
mutation: RootMutation//修改
})
1.以上是一个schema文件, 导出了一个GraphQLSchema的实例,GraphQLSchema接收两个参数,一个是query,一个是mutation。
2.RootQuery是一个GraphQLObjectType实例,name属性非必填,这个属性是最后生成接口文档的query接口名字。
3.fields是解析函数,在这里可以理解为查询方法。
4.getCategories就是我们定义查询的名称了。
5.getCategories中有个type,定义了你使用getCategories能查询到的所有字段。当前查询结果是一个GraphQLList类型,也就是一个数组类型,数组的每一项就是Category。
6.resolve中的内容为查询返回的数据。
客户端对应的查询语法:
query{
getCategories {
id,
name,
products{
id,
name,
}
}
}
1.首先进行第一层解析,查询类型是query同时需要它的子query名是getCategories。
2.使用getCategories的Resolver获取解析数据,第一层解析完毕
3.之后对第一层解析的返回值,进行第二层解析,当前getCategories还包含3个属性需要查询,分别是id、name、product。
3.1 id和name在type Category中为标量类型,直接返回getCategories中的对应属性值。id,name的解析结束。
3.2product在type Category类型中为对象类型,于是Granphql尝试使用Category的Resolver获取数据,当前field解析完毕。以此类推,直到所有的查询值都为标量的时候,查询结束。
因此上例中的查询结果为:
{
"data": {
"getCategories": [
{
"id": "1",
"name": "吃的",
"products": [
{
"id": "1",
"name": "浪味仙"
}
]
},
{
"id": "2",
"name": "喝的",
"products": [
{
"id": "4",
"name": "茶颜悦色"
}
]
}
]
}
}
网友评论