前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >激荡二十年:HTTP API 的变迁

激荡二十年:HTTP API 的变迁

作者头像
tyrchen
发布2022-12-05 14:30:33
1.7K0
发布2022-12-05 14:30:33
举报
文章被收录于专栏:程序人生

题记

这篇稿子断断续续写了有两周,期间还在公司做了一次 “A Tour to API Evolution” 的讲座,基本上就是把中文的稿子转译了一下。我之所以要研究这样一个主题,是想从 API 的历史中找到未来前进的方向,毕竟「读史使人明智,知古可以鉴今」。

这篇文章我所介绍的 API 的变迁,特指客户端和服务器之间运行的 HTTP API。一切其他协议的 API(如 TCP),或者服务器之间的 HTTP API(如 gRPC),不在深入讨论之列。

本文所涉及的时间,如果用大约修饰,那么是我记忆中的,可能并不准确的时间;否则,是我经过 wikipedia 验证后的较为准确地时间。

2005年之前:API 的狂野西部

早期的互联网是非常狂野的,没有所谓前端后端之分。PHP 开发者可以把从处理用户的 HTTP 请求,连接 mysql,组装 SQL 进行查询,将查询结果转换成 HTML,一路到 HTML 响应返回给用户的整个业务逻辑放在一个(或者若干个)如意大利面条般的脚本中。

2001 年,我还在上大二时,微软发布了 .Net framework 的第一个 RC。当时 .Net 声势浩大,微软在各个主要高校组织了夏令营,来选拔来年 Microsoft Asia 开发者大会的团队。当时我印象深刻的是,跟随着 .Net 一起新鲜出炉的 WSDL(Web Service Description Language),它「第一次」(也许)以公开协议的方式来描述通用的HTTP 客户端和服务器之间的 API。在 WSDL 的约定下,API 的请求和响应以 XML SOAP 的形式封装。

在那个狂野的,没有 API 的概念的时代,WSDL 简直就是一股清流。可惜它思想太超前,服务描述太繁杂,使得一个非常简单的 API 动辄生成成百上千行 XML 格式的 WSDL。在那个客户端和服务器能力还十分有限的年代,WSDL 几乎没有激出任何水花,就被扔到了历史的故纸堆中。

可能早期微软把太多的赌注放在了曲高和寡的 WSDL(以及服务发现协议 UDDI)上,其主打做 web 开发的 ASP.Net 一直不温不火,根本无法与红遍天的 PHP 相提并论。在 2005 年之前,可以说,(在 web 世界里),PHP 是宇宙中最好的语言。

2005-2010:从混沌到有序 — Ruby on Rails 横空出世

然而,成也萧何败也萧何,脱胎于 Web 开发的 PHP,与 Web 的亲和性是其优势,也是其后续没落的原因 —— 毕竟,当 Web 软件越来越复杂,需要跟越来越多的 web 以外的世界(比如操作系统)打交道时,跟其他通用的脚本语言,如 Python/Ruby 相比,PHP 就尽显劣势。

尤其是,当 Ruby on Rails(以下简称 rails)这个引领一个时代的 web 框架横空出世后,PHP 尴尬的发现,自己的优势,可能就只剩下多年来积攒的生态系统,以及在这个生态下滋养着的一大堆开发者了。

rails 是一个足以载入史册的框架:它把软件开发中的很多非常有益的概念、模式和思想(包括但不限于 ORM,CoC,MVC 等)糅合在自己体内,构建了一个强大同时非常易用的 web 开发系统。在 rails 下,哪怕你是个 web 开发的小白,在学习了 rails 的开发文档后,也能很快撰写出一套让很多 web 开发老鸟艳羡的系统。在 rails 诸多创新之中,要数 ActiveRecord 最为经验,它以简洁优雅的表述,颠覆了人们传统上对数据库的认知,并且几乎凭借一己之力,把 ORM 捧上了神坛。

随着 rails 一起成长的还有 XMLHttp object (俗称 Ajax)的标准化,以及 JSON 的广泛使用。其中,Google 通过其旗下的 gmail / google maps 大大促进了人们对 Ajax 的认知,而 PHP5 和 rails 3 则将 JSON 在广大开发者中推广开来,使其逐渐取代笨拙低效的 XML。有意思的是,Ajax 最初是 Asynchronous Javascript And XML,JSON 普及后,这个 XML 再也没人提及。

rails 的成功催生了一系列迷弟迷妹 —— 各个语言的,无论是高仿 rails,或者受 rails 启发的框架如雨后春笋般冒出,好不热闹。这其中,光是我深度使用过的框架就有:symfony,django 和 Phoenix framework。由 rails 刮起的 ORM 之风愈演愈烈,它几乎成为了 web 开发者访问数据库的唯一标准。渐渐的,存储过程(stored procedure / function)被雪藏,触发器(trigger)被遗忘,数据库复杂而迷人的权限管理被弃之不顾,取而代之的是用一个几乎具有 root 权限的用户来连接数据库,而权限的管理全部被前移到了应用层。这和 ORM 所倡导的「一套代码处理多种数据库」有莫大的联系。事实上,ORM 带给大家切换数据库的好处,可能仅限于开发环境用 sqlite,生产环境用 postgres 这样的便利。但从管理的角度,ORM 让开发者绕过 DBA(或者干脆不要 DBA)进行快速开发,对于小型项目,可以高效开发,且不需要构建数据库领域的专有技能,毕竟培养一个 web 工程师,两三个月的训练营就可以让一个素人很好掌握开发框架,进行「高效」 CRUD 开发;而培养一个合格的 DBA,需要整个计算机体系知识的沉淀。早年间 DBA 还是个热门的职位,后来在 rails 以及其一众小弟的推波助澜下,DBA 几乎在中小型企业中销声匿迹。

2010-2015:移动互联网 — API 飞上枝头变凤凰

现在回过头来看,2010年前后,也就是我创业做途客圈前后,算是近年来少有的互联网创新领域集中爆发的几年。这其中,最大的功臣要数乔帮主 2007 年推出的惊世骇俗的「三个」产品:初代 iPhone。它完全颠覆了我们对手机的认知,颠覆了对输入的认知,以及,颠覆了我们的生活。

随后,大获成功的 iPhone 4(及 4s)真正把我们的生活扯入了移动互联网时代 —— 作为当时最成功最流行的 3G 手机,iPhone 4让移动应用进入到主流用户的视野。由于移动应用拥有自己的 UI 层,不像浏览器那样,UI 层是由服务器返回的 HTML 渲染出来,因而移动端和服务器之间有着强烈的对简洁高效且标准化的 API 层的需求。在这种需求的催生下,REST(Representational state transfer)这个当时已不新鲜的概念渐渐从象牙塔走入了工业界。人们发现,与其自己随机指定一套 HTTP API 的规约,不如遵循 HTTP/1.1 规范,让 API 的表述和规范靠拢。这个时期,各个框架要么开始内建对 RESTful API 的支持,要么在框架之上,独立出一套专门为 API 优化的框架,比如 2012 年就比较成熟的 django REST framework:

也许是受到了移动互联网的冲击,也许是看到了客户端和服务器彼此隔离带来的巨大好处,web 开发也渐渐向 REST API 靠拢。在早期的 backbone.js 的引领下,web app 的 API 化在 react 发布后迅速升温,并在后续的几年得到了主流开发者的认可。到目前为止,纯服务器渲染返回 HTML 的 web 应用可能只剩下半壁江山。

这个时期,如雨后春笋般绽放的众多 REST API framework 给开发者带来的巨大好处是,你即便不掌握 HTTP/1.1 协议的细节,也可以做出像样的 API,来处理客户端和服务器间的交互。

然而,并不是所有的 API 框架都足够严谨,足够遵循协议本身。很多 API 框架,在处理复杂的协议流程时,要么会有自相矛盾的处理,要么把这些细节完全交由开发者处理。然而,你如何保证只热衷于进行 CRUD 的开发者能够正确使用 ETag 作为乐观锁(optimistic locking)进行条件更新(conditional update)呢?

在 web 世界不为人知的角落,Erlang 的 webmachine 尽着最大的努力来确保 API 的处理符合 HTTP 协议。得益于 erlang 强大的 pattern matching 的能力,webmachine 在内部构建了一张庞大的决策树,涵盖了 API 处理的每一个细节,连每个错误返回的状态码都精益求精。

(如果你对此感兴趣,可以 google:webmachine decision flow)

另一个小众语言 clojure 的小众框架 liberator 也把 webmachine 的这一思想学了过去,并发扬光大:

(请 google:liberator decision graph)

我曾经一度把玩过 liberator,相对于我当时在生产环境使用的比较流行的 eve 和 django rest framework 来说, liberator 真的是优秀很多。

然而,移动互联网不是小众语言和小众框架的战场。何况,API 毕竟是客户端和服务器共同的约定,在那个年代,服务端的严谨会给客户端带来不小的困惑:相较于 412 Preconditional failed 而言,客户端工程师更钟情于一招鲜吃遍天的 400 Bad request。

?-2016:我的第一次 API 工具的探索

由于在途客圈和 Juniper web security team 有了不少对 API 开发的思考和沉淀,我一直有心做一个自己的 API 开发框架。在加入 Tubi,理顺我们当下的 API 结构后,我便以 eve 和 liberator 为蓝图,nodejs restify 为基石,尝试着构建了一个 UAPI 系统,目的是以 pipeline 的形式处理 API 的流程,让公司的 nodejs 开发者只需要专注在业务逻辑,其它的交由框架完成:

UAPI 算是个成功的 API 系统,它在 Tubi 一直使用了六年多,直到现在还在局部使用。对客户端而言,它最大的好处是输入和输出都可以强制类型(如果定义了 validators 的话),这样,不符合要求的输入会在 API 处理流程很早的时候就被捕获,进而返回详尽的错误。

在 UAPI 演进的过程中,我也感受到了它的诸多局限和问题。其中最大的问题是:框架的使用者是开发者,而开发者如果没有得到充足的培训,会遗漏、误用、滥用框架的某些能力。比如在 UAPI 中,API 的类型安全不是强制的,因而有的 API 在一开始对 Request 中的各个部分做了类型检查,但随着 API 的迭代,往往新添加的 HTTP 头,并没有妥善定义相应的类型检查,于是开发者在业务逻辑中东一块西一块做各种校验,最终导致不优雅的,甚至混乱的表达。

UAPI 的详情我就不展开了,感兴趣的可以参考我之前的系列文章:再谈 API 的撰写 - 架构

也许在 UAPI 上我犯下的最大的错误,就是没有强制类型检查,把是否需要类型安全的选择交给了开发者。

2015-2020:类型安全 — 新的共识

并不只有我自己有类型安全的切肤之痛,似乎整个行业都发现了 RESTful API 在这一点上的不完善。2015 年,facebook 首先用开源的内部项目 GraphQL 向业界打出了意图取代 RESTful API 的一记重拳。GraphQL 从输入和输出入手,在 HTTP 协议之上定义了一套查询语言 —— 客户端和服务器之间需要定义好支持的 query / mutation / subscription 的 schema,以及输入和输出数据结构的 type。

GraphQL 提出了一个看待 API 的全新视角:客户端使用者可以根据需要灵活定义他们想查询的数据,而不需要看服务端老爷们的脸色。在固执的 RESTful API 的原教旨主义者眼里,API 应该严格对应资源,因而一个 app 页面如果包含三种不同的资源,那么它就要访问三个不同的 API 来获得结果。对客户端来说,这额外多了两个浪费用户宝贵等待时间的 roud trip,为什么不能一个查询就获得我想要的数据,且仅包含我想要的数据呢?

这个想法很有创意,但它忽视了灵活性带来的可能并不值得的复杂性。GraphQL 的理想情况一直没有很好地达成,因为服务端不可能为一个多层随意嵌套的查询去准备数据。同时 GraphQL 还有其他很多设计上考虑不周的问题,其中最让人诟病的是,对 HTTP 协议的无视,也就导致整个 HTTP 生态和 GraphQL 工作地很别扭,还有查询时 n+1 的问题(data loader 只是个特定场景的解决办法)。

其实仔细想想,GraphQL 并没有领先到足以完全让大家告别 RESTful API 的地步。其实 RESTful 服务器可以构建 proxy API 来访问若干其它 API,来解决一个 round trip 就能满足客户端的需求,同时也可以使用 partial response 来让客户端精确指定它想要的数据。这样下来,GraphQL 最重要的优势便荡然无存。

2016 年,google 开源了 gRPC。它使用 protobuf IDL 来解决输入输出的类型安全问题,并且采用 HTTP/2 来支持应用层的多路复用(multiplex)。gRPC 在设计时瞄准的就是 server-server 的使用场景,因而它可以使用二进制数据来达到最好的效率。由于我们这里只着重谈 client/server 的 API 演进,就不展开谈 gRPC。

2017 年,OpenAPI v3 问世,REST 的世界终于也有了自己的类型安全。然而 OpenAPI 并不强制输入输出的类型安全,这跟 UAPI 有同样的问题:随着公司 OpenAPI spec 的不断迭代,API 中某些新添加的字段,很容易被忽略,日积月累下来,问题会越来越多。

类型安全对 API 系统的意义不仅仅是输入输出有更加严格的校验,错误的输入能在很早的时候就被发现这么简单。它还打开了一扇新的大门:代码生成。无论是 GraphQL / gRPC,还是 OpenAPI,它们都可以根据 schema 生成客户端 SDK,甚至服务端的 stub 代码。

当然,写 schema 本身是一件很痛苦的事情,尤其是对于我们这些能写代码就不想写文档的开发者。于是,人们开始追寻取巧的办法:可不可以只写代码,然后通过代码来生成相应的 schema?

schema first, or code first, this is a question.

大部分支持 GraphQL 或者 OpenAPI 的框架遵从程序员的本性,让你可以专注于写代码,顺带生成相应的 schema。这是典型的 code first 的思维。

而 schema first 的代表要数 gRPC —— 你撰写 protobuf 定义,相应的编译器会替你生成代码。

这两种方案的背后,实际上是框架思维和编译器思维的较量。

在我看来,code first 背后的框架思维,就像地心说,它一开始很简单,很容易上手,但随后你就不得不添加越来越多的本轮和均轮来对模型不断校正,使其适应在发展变化中的正确性的保证。

而 schema first 背后的编译器思维,就像日心说,是「少有人走的路」,(因为要写解析器或者编译器)开头异常艰难,但一旦成型,日后会越来越轻松,只需在不断拓展编译器的边界。

2018:我的第二次 API 工具的探索

在使用过多种 code first 的框架来构建 GraphQL / OpenAPI 的系统后,我开始构思自己的下一个 API 开发工具:goldrin。

这一次,我的目标是:

  1. 定义一门「语言」,来描述我们的 API
  2. 撰写不同方向上的 Parser(Code generator),将其转换成特定场景的代码
  3. 将 Parser 构建在 build pipeline 中,可以一次 build,生成各种结果
  4. 生成的结果要能很方便地扩展,以及和系统里的其他部分整合

这可能是我在 arcblock 的征途中,除了 forge 框架外,另一个很有意义的成就。这个项目的目标如此宏大,某种意义上说也是为了弥补开发人员的不足,所以很多时候,受限的资源反倒更能驱动创新。

在这个目标的驱动下,goldrin 实现了从一个类似 ansible 的,用来描述数据类型以及在数据类型上允许进行的操作的 schema,构建出相应的数据库表的定义,GraphQL server 端实现,以及文档的定义。这套系统最大的好处是:无论是客户端开发者,还是后端开发者,都可以撰写几十行 YAML 就得到一个可以运行的,和数据库紧密连接的 API playground。然后你可以在此基础上不断调整,让 API 从原型一步步走到令人满意的,可发布的版本,期间几乎不用撰写代码(可能需要简单的 mock resolver)。当 API 的接口成型后,我们可以再撰写代码,重载特定的 resolver,使其拥有更高效,更优雅的实现。

如果大家对此感兴趣,可以 google:Use goldorin to build absinthe ecto enabled GraphQL APIs,看看我在 2018 年 ElixirConf 上的 lightening talk。很遗憾的是,由于当时我还想在 goldrin 中提供对 gRPC 的支持后再开源,导致这一项目一直没有开源,直到我离开。对于这个项目,我没有像 UAPI 那样留下一个系列文章,只有一篇短文:思考,问题和方法

2020:我的第三次 API 工具的探索

如果说 goldrin 是一个被外部环境倒逼出来的急中生智,quenya,则更多像是我在无拘无束的条件下,把我之前做过的诸多系统回溯一下,集大成的找乐子项目。这一次,我试图从 OpenAPI v3 spec 出发,构建一切可以自动化生成的代码,甚至包括 API 的测试。

quenya 说实话,在思路上并没有比 goldrin 进步多少,它的核心还是一个编译器,从 OpenAPI 中挖掘各种有用的信息,生成相应的代码。对此感兴趣的同学,可以看我的这个系列文章:构建下一代 HTTP API - 总览

2020-至今:低代码时代 — API 何去何从?

让我们快进到 2020 年。

低代码开发平台(Low-code development platform)虽然在 2014 年就被作为一个正式的名称被提出,但其开始打开局部市场,获得大规模融资,以及进一步的发展,也就是近两年的事情。低代码的概念其实一直挺模糊,但大家的共识是:用户可以无需太多编码,通过「描述」其需求(可以在 GUI 上操作,也可以是撰写某种简单易懂的 schema),就能够构建完全可使用的应用软件。低代码描绘了一个程序员之外的更广泛的人群可以构建应用程序的美好世界。

然而,有应用程序的地方,就需要 API,而构建 API,则离不开开发者的参与。虽然过去二十年,API 开发的自动化程度已经大大提升,但我们还没有到达一个可以完全自动生成 API 的阶段。这还怎么低代码?

如果我们重新审视 API 的作用,我们会发现,作为客户端和服务端数据的桥梁,API 解析客户端的请求,从服务端某个 data store(可能是数据库,也可能是其他服务的数据等),获取相应的数据,然后按照 API 的约定返回合适的结果。

既然 API 的目的是提供数据,而数据往往有其严苛的 schema,同时 API 的 schema 大多数时候就是数据 schema 的子集,那么,我们是不是可以从数据 schema 出发,反向生成 API 呢?

乍一看,这个思路和我之前做的 goldrin 类似,但 goldrin 定义了新的「语言」,由外及内地生成 API 以及数据的 schema,而这个想法是,以数据库 schema 为单一数据来源,由内及外地生成 API schema,甚至 API 本身。

这并不是一个新的思想,早在 2015 年,postgREST 就开展了类似的尝试,只是这种离经叛道的思路和那个 ORM 还如日中天的时代格格不入。在 DBA 几乎绝迹于江湖后,有哪个初创企业会把自己的后端围绕着一个特定的数据库(postgres)构建,并且几乎用尽这个数据库每一个非标准的功能,完全不考虑可迁移性呢?

再加上 postgREST 是用 haskell 这样一门小众的语言开发,更使得好奇它的人多,而使用它的人少之又少。

简单介绍一下 postgREST 的思路。使用 postgREST,开发者只需正常定义数据库中的表,视图,函数,触发器等,并为它们的使用权限赋予相应的角色即可。postgREST 可以根据数据库的 infoschema,掌握详细的 metadata,并用这些 metadata 来验证 API 的输入,也就是 Request,如果验证通过,会根据 Request 生成相应的 SQL 查询,然后把结果序列化成客户端需要的结构,以 Response 返回。举个例子,对于这样一个 API 请求:GET /people?age=gte.18&student=is.true ,postgREST 会验证数据库中包含 people 表或者视图,并且其含有 age / student 这两个字段,前者是整型,后者是布尔型。如果一切符合,并且用户具备 people 表或者视图的 SELECT 权限,那么它就会生成 select * from people where age >= 18 and student = true 这样一条查询,返回相应的 JSON(默认客户端 accept: application/json)。

postgREST 还跟 postgres 的 RLS(Row Level Security)深度绑定,来解决用户个人信息安全访问和更新的需求。比如用户只能修改自己的帖子,但可以读别人的帖子这样的业务需求,如果没有 RLS,很难从数据库级别直接安全地实现。

postgREST 这样一个小众的工具进入到很多人的视野,还要归功于 supabase B 轮八千万美金的巨额融资。它为 postgREST 提供了 GUI,摇身一变成为 firebase 的挑战者,DBaaS 新生代的翘楚。

另一个有着同样思路,但采取了不同路径的产品 Hasura,今年早些时候 C 轮融了一亿美金。与 supabase 背后的 postgREST 不同的是,Hasura 把宝押在了 GraphQL。Hasura 试图回答一个问题:有没有可能把 GraphQL 的 query 一对一转换成 SQL 语句?

我们知道 GraphQL 查询会被编译成 Graph AST,而 SQL 查询会被编译成 SQL AST,所以上述那个问题就变为:Graph AST 可以被安全高效地转换成 SQL AST 么?

看看 Hasura 的天量融资,你就可以猜到,这条路走得通。撰写自己的编译器虽然是一条「少有人走的路」,但一旦走通,其迸发的能量是巨大的,而且有意想不到的效果。前面提到的 GraphQL 令人诟病的 n+1 的问题,在 Hasura 面前都不是是个事,因为引发 n+1 问题的嵌套查询,翻译成 SQL 就是一个 INNER JOIN,于是 n+1 问题就这么被悄无声息地解决了。

那么,Hasura 是如何实现这一切的呢?我并没有深入研究,然而当我打开 Hasura graphql-engine 的源码,惊奇的发现,除了 20 多万行 typescript/javascript 代码,和 3 万多行 golang 代码外,它还有 13 万行的 Haskell 代码。莫非,Hasura 也从 postgREST 那里「偷师」?稍稍查询一下,发现代码中确实有一些 postgREST 的痕迹。

2022:我的第四次 API 工具的探索(头脑风暴)

在仔细研读了 postgREST 的用户文档后,我大概摸清了它的产品思路。于是我一时技痒,展开头脑风暴,思考如果做一个类似的工具,我该怎么做?

首先,我并不喜欢 postgREST 的查询方式,它的 DSL 在我看来有些蹩脚。我希望通过 x-fields 和 x-filter 这两个 HTTP 头,来实现 postgREST 里 querystring 所表达的内容:

对于 x-fields,它有略微复杂的,但继承自 postgREST 的字段选择语法,我可以使用一个 parser combinator(比如 Rust 下的 nom)来解析它,这样就可以清晰地知道,字段名如何重命名,以及字段来自于哪张表(如果有 JOIN 的话)。x-filter 我还没想好如何表述,但我觉得 SQL 中的表达式就够用了。对于 x-filter,我们可以也用 parser combinator 来解析,或者干脆使用某个SQL 解析器(比如 Rust 下的 sqlparser)解析。解析出来的 metadata 可以和数据库中的 infoschema 比对,来验证请求的合法性,这一点和 postgREST 完全一致。最终,从 x-fields / x-filter 中解析出来的内容,连同 rang 头(用于分页)一起,就可以构建出一个完整的,合法的 SQL 查询,最终得到返回的结果。

到目前为止,这个系统和 postgREST 如出一辙,没什么了不起的。

平心而论,我觉得这样的 API 系统,用于内部系统,还说得过去,但用于外部系统,就过于暴露数据 schema 的细节,同时让 API 的接口和数据本身过于耦合。这样一来带有安全隐患(很容易被嗅探),二来不利于在 API 接口保持不变的情况下升级数据 schema。对此,postgREST 给出的答案是使用 view 来隔离 table schema 的细节,但我觉得还不够完善。我需要一个能够在外部看来,更加自然,更加简单的 API。

在计算机的世界里,这样的问题往往可以通过添加一个新的层级来实现。我并不需要改动已有的设计 —— 它对于内部系统来说还是相当不错的设计,我只需要在这个设计之上,迭加一层。于是我有了这样的思路:

开发者可以使用 CREATE API(我胡诌的新 SQL 语法) 来创建一个 API 的描述。这个 todos API,包含两个参数:来自 auth header 的 jwt token,以及来自 querystring 里的 completed。API 的 metadata 中包含了一些详尽的配置,以及 API 的参数如何作用到配置中。有了这样的一种 API 配置,用户可以用图中更自然地方式访问 API,而 API 自身没有暴露任何数据库的逻辑。

整个过程,比之前的方案多了个 API 的定义过程,由于使用的是描述性语言,所以,很难误用,并且以后还能很方便的用 GUI 来表述,也算是一种低代码了。

看到这里,有经验的同学可能会质疑:API 的数据源又不止于数据库,如果数据来源于 gRPC 服务器,那又该如何?

好问题!此刻我们需要修改 CREATE API 的描述,使其明确表达其数据源是什么。在下图的例子里,数据源是 grpc_todos:

而 grpc_todos 由 CREATE SOURCE 来定义:

代码语言:javascript
复制
CREATE SOURCE grpc_todos WITH JSON({
  "source": "wasm",
  "wasm": {
    "lib": "todo.wasm",
    "fn": {
      "name": "get_todos",
      "args": [...],
      "return": ".."
    }
  }
});

再一次地,我们看到,使用编译器的思路去解决问题,是多么地舒服:我们可以不断扩展新的语法,撰写新的解析器去处理问题。这非常符合 open-close 原则。

这里 source 我使用 webassembly,并不是为了装 B,而是我希望这样的工具就像 postgREST 一样,你不需要,也无法对其二次开发。如果需要扩展,那么 webassembly 或者 JS 就是最佳的选择。它可以集成 wasmtime 来处理 webassembly,也可以集成 deno_core 来安全地支持 typescript/javascript 扩展。

以上关于第四次 API 工具的探索的一切不靠谱的想法,都只存在于我的脑海中,我的 excalidraw,以及我的 PPT 里。

本来这篇文章应该在上周末发表出来,可是我一时技痒,把周末可用的时间匀给了代码实现,于是我在撰写了(主要是通过 psql -E 偷师 psql 命令是如何查询的)上百行 SQL,从postgres 中获取关于 relation / function / columns / constraints 的 infoschema,将它们构建成 materialized view,然后利用这一信息,自动构建了简单的,没有任何安全限制的 API:

整个过程还是蛮有意思的 —— 尤其是我之前并没有很深入地了解 postgres 的 infoschema,这下仿佛爱丽丝掉进了兔子洞,整个世界都不一样了。

展望未来

过去二十年,API 的发展从无序走向有序,从自由发挥走向遵循协议,从无拘无束走向谨遵类型安全,每一次进化都让 API 从构建到部署到退休的整个生命周期变得更加成熟,同时 API 撰写的难度和所需要的技巧都在不断下降,自动化程度不断上升。然而,就像金庸武侠的最高境界是「无招胜有招」一样,无论API 代码再如何简洁,只要代码在那里,就会有无穷尽的成本,因此,最好的 API 系统是不用撰写一行代码就能提供服务的 API 系统。我们欣喜地看到,postgREST 和 Hasura 这样的项目,在努力往这个方向发展。也许在不久的将来,我们可以通过数据的 schema,倒推出 API 的 schema,再进一步倒推出使用这些 API 的模板化的 UI 页面。这其中,80% 的情况我们可以直接使用自动生成的功能,剩下的 20% 需要稍作定制便可完成。我希望,也坚信,未来 10 年,我们有望看到这样子的 API 系统。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题记
  • 2005年之前:API 的狂野西部
  • 2005-2010:从混沌到有序 — Ruby on Rails 横空出世
  • 2010-2015:移动互联网 — API 飞上枝头变凤凰
  • ?-2016:我的第一次 API 工具的探索
  • 2015-2020:类型安全 — 新的共识
  • 2018:我的第二次 API 工具的探索
  • 2020:我的第三次 API 工具的探索
  • 2020-至今:低代码时代 — API 何去何从?
  • 2022:我的第四次 API 工具的探索(头脑风暴)
  • 展望未来
相关产品与服务
Serverless HTTP 服务
Serverless HTTP 服务基于腾讯云 API 网关 和 Web Cloud Function(以下简称“Web Function”)建站云函数(云函数的一种类型)的产品能力,可以支持各种类型的 HTTP 服务开发,实现了 Serverless 与 Web 服务最优雅的结合。用户可以快速构建 Web 原生框架,把本地的 Express、Koa、Nextjs、Nuxtjs 等框架项目快速迁移到云端,同时也支持 Wordpress、Discuz Q 等现有应用模版一键快速创建。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档