• 初识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资料

  • 相关阅读:
    [React Testing] Intro to Shallow Rendering
    人物-IT-马化腾:马化腾
    人物-IT-雷军:雷军
    Less:Less(CSS预处理语言)
    文学-谚语-英文谚语:英文谚语
    文明-墓-太阳墓:太阳墓
    地理-撒哈拉之眼:撒哈拉之眼
    生物-海底人:海底人
    地理-蓝洞:蓝洞
    文明-根达亚文明:根达亚文明
  • 原文地址:https://www.cnblogs.com/Jingge/p/12916901.html
Copyright © 2020-2023  润新知