GraphQL 一直是热度很高的技术,就以技术社区掘金上的数据为例,GraphQL话题的关注者是 8429,文章数量是303,远超 RESTful 的 2366 关注者和 74 篇文章。
官方的描述语也很吸引人,将 Graph QL 描述成一个万能接口,想要什么格式的数据可以直接通过查询语言去取,不需要再请求后端工程师进行修改。
基于以上种种“广告”效应,我们团队也开始在小项目上尝试使用 GraphQL。
架构选型
使用 GraphQL 有两种架构模式:
- 直接用 GraphQL 取代 REST API。这种方式对代码的改动比较大,但也更为彻底。但对于后端工程师来说有一定的学习成本。
- 重新搭建一个 GraphQL 服务器作为 API 网关,用来转发请求给 REST API 服务端,这种方式不会改动已有代码。但项目的复杂度会增加,因为新增了 API 设计风格和网关服务器。而且也会增加前端工程师的工作量,前端要一边和后端调试 REST API 还要一边和前端调试 GraphQL API。
由于并不希望增加项目复杂度,所以采用了第一种架构模式,即将已有的 REST API 改成 GraphQL API。
GraphQL 生态
初次通过官网了解 GraphQL,考虑到后端库支持十几种语言,觉得适用性应该不错。而且关注者众多,文章分享也不少,遇到问题时解决方案应该很多。
但实际使用后,这种感觉发生了反转。
首先虽然支持多种后端语言,但只有 graphql-js 是官方的亲儿子,其他的都是第三方开发的,代码质量和后续的维护性都令人担忧。
这不是重点,重点是这些后端库中,只有 JavaScript 和 Java 支持 SDL,其它的都要通过类的方式模拟实现。对比一下就可以感知两者的差异了,下面是 JavaScript 和 Python 的示例代码。
// JavaScript
var schema = buildSchema(`
type Query {
hello: String
}
`);
var resolver = { hello: () => 'Hello world!' };
# Python
class Query(graphene.ObjectType):
hello = graphene.String(name=graphene.String(default_value="World"))
def resolve_hello(self, info, name):
return 'Hello ' + name
在 JavaScript 中,我们可以以字符串的形式定义 schema,然后编写类型对应的 resolver,来告诉 GraphQL 怎么返回相应的数据。而在 Python 中必须以类的形式定义类型,而且 resolver 必须和类型字段对一一对应。 这就带来了两个问题:
- 编写繁琐。当遇到字段较多的复杂对象时,你是愿意一个个字段的编写 resolver 函数,还是愿意一次性将整个对象结果返回?而且就以常见数据库CRUD操作的情况而言,一次性返回整个对象更符合实际开发场景。
- 不可移植。如果采取字符串形式,schema 可以被抽取成 gql 文件进行复用,还可以很方便地被移植到其它支持 GraphQL 语法的语言或工具上,但 Python 这种和语言绑定的写法就没法办到了。
正是考虑到这一点,还是改为了使用官方推荐的 Node.js 来重构项目。
没有 apollo
目前来看大部分 GraphQL 项目都使用 apollo,apollo 支持平台比较多,文档也算丰富,基本上能做到开箱即用。
但尴尬的是阿里云函数计算并不支持,所以只能通过更底层的 graphql-js 来实现。带来的麻烦就是查找资料相当不方便,大部分文章都是基于 apollo 使用的经验分享,对于 graphql-js 的讨论则很少。
比如在开发中碰到一个了小问题,就是需要在查询时提交参数。 使用 apollo 时,前端可以很简单地在 payload 中提交一个 JSON 对象,query 属性值为查询字符串,variables 属性值为参数对象。
{
query: "{ user($id: ID!) { name } }"
variables: {id: "1231232"}
}
后端处理逻辑都被 apollo-server 封装好,不用进行任何处理,定义好 schema 和 resolver,apollo 会自动处理。
但由于不能使用 apollo-server,所以必须手动解析 query 和 variables。搜索 graphql-js 官方文档得到只是如何使用 variables ,完全没提到服务端如何解析。网上文章大多也是把官网的内容抄一遍。
后来查看 apollo 源码知道答案,原来还是依赖 graphql-js 的本身的 API。
graphql({
schema, // schema定义
source, // 查询语句
variableValues, // 前端参数
typeResolver // 解析函数
})
})
虽然只是一个小问题,但也体现了文档的不完善和解决方案的缺乏。
总结
从代码仓库 graphQL-js的0.1.1版本发布时间来推算,GraphQL 发布时间大致在 2015 年,至今已有 5 年。这个时间已不短,但目前来看,仍然是处于叫好不叫座的情况。
通过对 GraphQL 的使用,不禁开始思考 GraphQL 发展缓慢的原因。 从后端语言的完善程度来看,只有 JavaScript 最具有实用性,对于其它主流后端语言,既没有成熟的库,也存在一定的学习成本,还增加了工作量。所以 GraphQL 就变成了前端工程师自嗨的游戏,API 网关的形式成为了主流。
随着技术的不断发展, GraphQL 的优势其实也在被削弱:
- 对于 GraphQL 最引以为傲的可定制化查询功能,即使不通过 GraphQL 语句也可以实现,比如设置过滤字段。而且这个功能在某些场景下会变得比较繁琐,比如想获得一个复杂对象所有字段,那么需要把这个对象的所有字段一个个写出来。
- 而至于聚合 API 来减少请求数量在 HTTP/2 似乎也不刚需,因为 HTTP/2 并发性本身就比较强,请求合并和拆分无太大区别,某些场景下,拆分请求逐步加载数据用户体验可能更好。
- GraphQL 的模式校验功能 json-schema 规范以及其它库都可以提供。
- Schema 即文档倒确实是个亮点,但却不足以点亮 GraphQL 的前景。
- 至于 API 工具,GraphQL 无论在数量还是在质量上都是比不过 REST API 的。
整体上来看,对于大多数项目而言,并没有足够的理由使用 GraphQL。 而对于前后端开发而言,更像是一个零和游戏,把 GraphQL 服务架设在网关服务器需要前端工程师多写代码,后端服务改造成 GraphQL 则是增加后端工程师的工作量,除非项目后端采用的 Node.js 开发,那么使用 GraphQL 或许很有优势~