初识graphql

最近在做公司的一个内部项目,其中用到了graphql技术,通过这些天的学习对graphql有了大概的认知,这篇文章算是对graphql的总结,本文主要分以下四部分部分。

graphql简介

什么是Grphql?

GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。它是一个新的API标准,由Facebook开发和开源,提供了比REST更高效、灵活、强大的替代方案。

上述定义是官方文档给出的,看完之后可能一头雾水,api不是服务端定义好供客户端调用的吗?基于API的查询是什么鬼?

传统的服务端会提供很多接口,每个接口对应一个url,接口的返回值是定义好的,客户端被动的接收所有的数据。 例如一个查询患者详情的接口它的返回结果包含了如下的信息:



可以看到这个接口返回的字段非常多,有些字段前端用的到,有些字段对前端来讲完全没用,这就是数据过载的问题,这样会有损应用的性能(需要更长时间下载,解析更多的数据)。

Graphql服务能够很好的解决这个问题,“用于API的查询语言”表明graphql允许客户端向服务端接口发送查询请求,客户端需要什么字段就请求什么字段,graphql服务会以一致的方式返回这些数据。举个例子:有个患者查询的接口,客户端客户端只想查询患者姓名和年龄,用graphql的话只需下面的查询就能得到想要的结果。

query {
    patient(id: '1') {
        name,
        age,
    }
}

接口返回结果:
{
    data: {
        patient: {
            name: '张三',
            age:25
        }
    }
}

什么原理呢?

graphql服务是基于类型系统的,服务端会事先定义好schema,schema描述了服务所支持的查询,变更,订阅操作等,graphql服务收到客户端请求后,会逐字段遍历,找到schema中对应的字段,然后执行这个字段的“解析器”,通过解析器获得数据返回给客户端。

下面是一个graphql服务 schema的定义:该graphql服务有一个名称为patient的查询,查询的返回结果的类型是Patient;还有一个addPatient的变更操作,操作返回结果是修改后的患者信息。

// graphql.schema

type Query {
    patient(id: String): Patient,
}

type Mutation{
    addPatient(name: String, age: Number, phone:String):Patient
}

type Patient{
    name: String,
    age: Int,
    ...
}
schema {
    query: Query,
}

还是以客户端请求查询患者为例,客户端发送下面的查询请求

query {
    patient(id: '1') {
        name,
        age,
    }
}

服务端收到请求后,遍历到patient字段,然后调用patient的解析器,解析器resolver通过读数据库查询数据,或者调用第三方服务返回数据。

graphql服务的执行过程大概是这个样子

image

与REST服务对比

  1. graphql服务允许客户端选择返回的字段。

    它解决了数据过度获取的问题,在实际场景中可能对多端产品的接口复用有一定的帮助。例如公司的产品可能会有web端,安卓,小程序等等,不同的客户端同一功能展示的效果可能不一样。就医生列表页面而言,web端可能需要展示id、姓名、医生类型、而小程序医生列表可能只需要展示姓名、头像、诊疗服务费等。

    针对这种情况,服务端要么提供一个返回所有字段的接口供所有客户端调用,要么提供多个接口供不同到客户端调用。然而这两种方式都有他的弊端,第一种方式就会导致过度获取问题,客户端得到了大量无用的数据,这样会有损应用的性能(需要更长时间下载,解析更多的数据)。第二种方式则会造成后期维护成本的增加。而graphq这种基于接口查询的方式能很好的解决这个问题。

  2. graphql服务允许客户端只通过一次请求就得到所有需要的数据。

使用REST API,通常会过访问多个接口来收集数据,例如我们中控系统中的耗材详情页面,除了调用接口查询耗材详情信息,还会调接口查询一些枚举下拉框的数据, 如包装单位、税务分类等等。如果用graphql只需调用一次接口就能查询所有的数据

query {
    cousumble(id) {
        name,
        alias,
        ...
    },
    // 查询库存单位
    units() {
        ...
    },
    // 查询分类
    category() {
        ...
    }
}
  1. graphql使用强类型系统定义API,服务支持什么操作都在schema定义好了,schema就相当于服务端和客户端之间的契约,一旦定义好前后端团队便可独立的工作。
  2. rest服务中,查询数据一般用get请求,添加修改数据一般用post,put请求,删除数据一般用delete请求。而graphql所有的操作都是post请求。
  3. rest服务中,每一个接口对应一个url,而graphql服务中只有一个url,通常是/graphql。

graphql的几个核心概念

1、Schema和类型

Schema定义了GraphQL API的类型系统,它完整描述了客户端可以访问的所有数据(对象、成员变量、关系、任何类型)。客户端的请求将根据schema进行校验和执行。

graphql有自己的语言来定义schema即SDL(schema definition language)。

2、对象类型

对象类型是 GraphQL schema中的一个最基本的组件,它就表示你可以从服务上获取到什么类型的对象,以及这个对象有什么字段。可以简单把它理解为model的定义,下面是用SDL定义对象类型的例子。

type Person {
  name: String!
  age: Int!
  posts: [Post!]!
}

这样就定义了一个名为Person的对象类型,他有两个字段,字段name和age是标量类型,感叹号表示非空。中括号表示是数组类型,所以posts是个数组类型,且非空,数组中的每一项也不为空。

Person中我们只定义了三个字段,这意味着在一个操作 Person类型的 GraphQL 查询中只能出现 name,age 和 post 字段。

字段的数据类型只有两种可能:要么是标量类型,要么是个对象类型。

标量类型

一个对象类型有自己的名字和字段,而某些时候,这些字段必然会解析到具体数据。这就是标量类型的来源:它们表示对应 GraphQL 查询的叶子节点

graphql的自带的标量类型有5种:Int、Float、String、Boolean,Id。

当然我们也可以自定义新的标量类型,例如定义一个Date类型。

scalar Date
枚举类型

枚举类型是一种特殊的标量,它限制在一个特殊的可选值集合内。这让你能够:
验证这个类型的任何参数是可选值的的某一个
与类型系统沟通,一个字段总是一个有限值集合的其中一个值。

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

3、查询与变更

schema内有两个特殊的类型:Query,Mutation。这两个类型和常规对象类型无差别,它们之所以特殊,是因为它们定义了每一个 GraphQL查询的入口。graphql服务支持的查询操作都定义在Query对象类型中,修改删除操作都定义在Mutation对象类型中。

定义查询

Query类型确切定义了客户端可以针对您的数据图执行哪些GraphQL查询(即读取操作)。它类似于对象类型,但其名称始终为Query。

type Query {
  getBooks: [Book]
  getAuthors: [Author]
}

此Query类型定义了两个可用的查询:getBooks和getAuthors。

定义变更

type Mutation {
  createPost(post: PostAndMediaInput): Post
}

input PostAndMediaInput {
  title: String
  body: String
  mediaUrls: [String]
}

查询可以传递参数,如果参数特别多可以定义一个输入类型,另外graphql的类型系统还支持接口定义等等,更多关于graphql的语法信息可参考官方文档

4、解析器

前面提到的schema只是定义了graphql服务支持的操作,他描述了允许客户端执行哪些查询,可以从服务器上获取哪些类型的数据,以及这些数据之间的关系。服务端收到请求后具体怎么把数据返回给客户端,算法的核心非常简单:逐字段遍历查询,为每个字段执行“解析器”。这篇文章有关于resolver详细执行过程的介绍。

graphql服务常见的三种架构

作为一种服务,最重要的就是对数据的操作,graphql服务可以直接操作数据库包括mysql,mongo等,也可以集成第三方服务或其他微服务。以下是常见的三种架构:

  1. graphql服务直接读取数据库


  1. graphql服务仅作为一个代理层,客户端通过这个代理层访问第三方或者旧系统的服务。


这张图有没有很眼熟?是不是和 BFF的理念十分贴合?

  1. 混合模式,即能访问数据库,又集成了第三方服务或旧系统

混合模式是将以上两种方法结合起来,构建一个即能连接数据库又与旧系统或第三方系统通信的GraphQL服务器。

附上一些Graphql资料

posted @ 2020-05-19 14:48  Jingge  阅读(763)  评论(0编辑  收藏  举报