背景

常见的 RESTful API 中,每个 API 负责请求一种类型的对象,一个页面往往需要多次请求接口,且经常存在依赖关系,无法并行发送请求。在工作中是否经常遇到这个场景:
产品:本次需求我们需要在页面上新增 *** 展示。
前端:目前后端提供的接口中没有返回,需要新增字段。
后端:这个接口现在多方在使用,需要评估是否会对其他使用方有影响,且目前排期已满,需要等到 **** 版本才能做。
产品:Boss 说明天要上线……

如果是个人项目,可能这个需求一两个小时就能完成,但如果是公司项目,这样一个小小的需求,动辄花费的时间要上周才能上线。那么,我们是否可以通过某种手段,将接口的返回值从静态变成动态,即调用者来声明接口返回什么数据,来进一步解耦前后端的关联?于是,Facebook 在 2016 年 9 月,GraphQl 正式进入生产环境。GraphQl 的联合创建者 Dan Schafer 认为:

“GraphQL 是一种查询语言,用于我们在客户端和服务器之间转移合约的 API,允许服务器说 ‘这些是我公开的能力’,并允许客户端以一种方式来描述它们的要求,而这种方式最终使产品开发人员能够构建其希望创建的产品。”

在后台,它高速 API 如何把检索到的数据呈现给客户端,是的开发人员能够发出精确的数据请求,不多也不少。

GraphQL 的使用

1.几个重要的概念
这里先简单介绍几个基础概念,其余概念可以在 GraphQL 官网了解

https://graphql.cn/learn/queries/ 

1.1 操作类型 Operation Type
GraphQL 的操作类型可以是 query、mutation 或 subscription,描述客户端希望进行什么样的操作
query 查询:获取数据,比如查找,CRUD 中的 R
mutation 变更:对数据进行变更,比如增加、删除、修改,CRUD 中的 CUD
substription 订阅:当数据发生更改,进行消息推送

1.2 对象类型和标量类型 Object Type & Scalar Type
如果一个 GraphQL 服务接受到了一个 query,那么这个 query 将从 Root Query 开始查找,找到对象类型(Object Type)时则使用它的解析函数 Resolver 来获取内容,如果返回的是对象类型则继续使用解析函数获取内容,如果返回的是标量类型(Scalar Type)则结束获取,直到找到最后一个标量类型。
对象类型:用户在 schema 中定义的 type
标量类型:GraphQL 中内置有一些标量类型 String、Int、Float、Boolean、ID,用户也可以定义自己的标量类型。

1.3 模式 Schema
它定义了字段的类型、数据的结构,描述了接口数据请求的规则,当我们进行一些错误的查询的时候 GraphQL 引擎会负责告诉我们哪里有问题,和详细的错误信息,对开发调试十分友好。Schema 使用一个简单的强类型模式语法,称为模式描述语言(Schema Definition Language, SDL)。

2 实战
Python Graphql 入门示例,采用 Flash 框架,API 使用 GraphQL 实现一个 web 服务,提供书籍的查询与添加功能。

2.1 安装模块

pip install graphene
pip install Flask-GraphQL

2.2 API 设计
创建文件 books.graphql

type Book{
    id: ID!
    name: String!
}

type Query{
    books: [Book!]!
}

type Mutation{
    add(name: String!): Book!
}

2.3 web 服务

from flask import Flask  
from flask_graphql import GraphQLView  
import graphene

app = Flask(__name__)

books = []

class Book(graphene.ObjectType):  
    """ Book
    """

    id = graphene.ID(description="book ID")

    name = graphene.String(description="book name")

create = lambda id, name: Book(id=id, name=name)

books.append(create(1, "The First Book"))

class Query(graphene.ObjectType):  
    """ query your books
    """

    books = graphene.List(Book, description="list books")

    version = graphene.String(description="version")

    def resolve_books(self, info):
        return books

    def resolve_version(self, info):
        return "v0.1"

# Mutation

class AddBook(graphene.Mutation):  
    """ Add books
    """
    Output = Book

    class Arguments:
        name = graphene.String()

    def mutate(self, info, name):
        book = create(len(books) + 1, name)
        books.append(book)
        return book

class Mutation(graphene.ObjectType):  
    """ mutate books
    """
    add = AddBook.Field()

schema = graphene.Schema(query=Query, mutation=Mutation)

app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql',  
    schema=schema, graphiql=True))

app.run(port=4901, debug=True)

代码分析:

  1. books 是一个全局变量,用于保存所有的 books,可把它当做数据库,很多示例都有数据库,为了保持简单,最方便运行,所以,我只用了这么一个变量
  2. Query 是比较简单的,语义很清晰,和 API 文档不同的是,在 Query 中增加了一个 version,这是为了简单对比一个 restful ,如果是 restful 的话,我们要获取 books 和 version 这两个资源,需要两个 API,但 graphq 只需要一个
  3. Mutation 分为两步,第一步定义一个 Mutate,里面必须要有 mutate 方法,第二个创建 Mutation,用于组合所有的 Mutate。
  4. /graphql 是给我们提供一个可视化的 API 展示,类似 Swagger,当然,生产环境就不要加这个了,加在开发、测试环境即可

运行代码
python server.py

2.4 查询
使用 curl 做测试,大家也可以尝试使用界面,更直观

http://127.0.0.1:4901/graphql\?query\=query%20%7Bbooks%20%7Bid%20name%20%7D%20version%7D

总结

我们上面介绍了 graphql 的优势,我们也介绍一下它的劣势及讨论一下为什么没有流行起来。

第一,Facebook 从来没有公开自己的 GraphQL 后端设计,使得大家必需要用第三方的,但体验显然不如我们在 Facebook 内部使用 GraphQL 好。我上面说了,数据必需已经以图的数据结构进行存储才有优势。Facebook 内部有非常好的后端做好了这件事情,而且还内置了基于隐私设置的访问控制。例如说你发的帖子有些是所有人可见的、有些是好友可见的、有些是仅同事可见的,我在打开你的页面时 Facebook 有一个中间层保证了根据我和你的关系我只能看到我该看到的帖子。GraphQL 在这一层之上,所以无论 GraphQL 怎么写我都不可能看到我不该看到的信息。

第二,并不是所有场景都适用于 GraphQL 的,有些很简单的事情就应该用 RESTful API 来实现。Facebook 内部用户增长部门的很多 API 都还不是 GraphQL,因为没必要迁移到 GraphQL。用户增长部门的 API 处理新用户注册、填写短信验证码之类的事情,这些事情都是围绕着一个用户的具体某项或多项信息发生的,根本没有任何图的概念。可以强行写作 GraphQL,但得不到显著的好处。既然老的 API 早就写好了,需要的时候做一些小改动,但没必要重写。

第三,GraphQL 尽管查询的数据是图状数据结构,但实际获得的数据视图是树状数据结构。每一个 GraphQL 查询或更新都有自己的根节点,然后所有的数据都是从根结点展开出去的。查询后获得的数据如果要在前端重新变回图的状态,那前端就不能简单地缓存查询得到的数据,必须用对用的 GraphQL 存储库,然后通过顶点的 ID 把不同节点之间的某些边重新连接起来。

而其没有流行起来的原因主要在于,GraphQL 的利好主要是在于前端的开发效率,但落地却需要服务端的全力配合。如果是小公司或者整个公司都是全栈,那可能可以做,但在很多前后端分工比较明确的团队里,要推动 GraphQL 还是会遇到各种协作上的阻力。

参考
RPC、RESTful API、GraphQl 三者对比
Graphql Spec


↙↙↙阅读原文可查看相关链接,并与作者交流