GraphQL

2020-06-14  本文已影响0人  倩倩_a570

一、什么是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": "茶颜悦色"
          }
        ]
      }
    ]
  }
}
上一篇 下一篇

猜你喜欢

热点阅读