TypeScript 实验: withtyped - 类型安全的零依赖 RESTful 全栈框架,写 API 自动推导客户端类型并生成 OpenAPI 接口

2022-11-20 14:48:58 +08:00
 pseudo

契机

结果

考虑到 RESTful 的兼容性和强类型带来的好处,花了几天研究了一下 KoaJS 的源码,并做了一些类型体操后弄出了一个 POC (proof of concept):

https://github.com/withtyped/withtyped

效果如图:

一些亮点

细节

写一个接口可以自动根据路径推导参数:

根据定义的返回值类型限制调用类型:

在客户端一切都能自动推导:

仅需一个函数生成 OpenAPI 接口:

router.withOpenApi()

结尾

项目尚处实验阶段,还请大家拍砖

3106 次点击
所在节点    分享创造
20 条回复
wdhwg001
2022-11-20 16:49:29 +08:00
牛逼,虽然还是觉得 NestJS 的注解式写起来更爽一点…
pseudo
2022-11-20 17:00:22 +08:00
@wdhwg001 哈哈,我和你正好相反,看到 decorator 就放弃了
amlee
2022-11-20 18:16:13 +08:00
不知道我理解的对不对,好像 python 的 fastapi 跟你这是一个套路。
一个框架用强类型的函数自动生成 api ,这样可以适配 OpenAPI ,自动生成 Swagger 文档,然后客户端 sdk 也就可以自动生成。
用强类型搞一条龙了属于是。
debuggerx
2022-11-20 19:21:32 +08:00
同样的追求,我用 dart/flutter 也实现过类似的效果
https://github.com/debuggerx01/dde_gesture_manager#faq
nomagick
2022-11-20 19:32:17 +08:00
楼主加油,大方向对了,具体实现上还是差点。和你另一个 oidc 项目一样,大方向对了,实现上差点。

目前 TS+node.js 生态里面确实缺少一个 fastapi 类似物,并且写一个 fastapi 类似物是可能的,我手上就有,但不开源。
现在开源的没一个能打的,那些个接口没法说是生成,全部是手动描述,和代码是割裂的,等同于注释文档,需要单独维护。你这个现在有一点点生成的意思了。

给你几点建议
首先,类型信息是以 Dto 为单位组织的,你用的 zod 库就是这个角色,它的完全体应当是一个类

第二个,Decorator 是最基本的,没有 Decorator 你的类型元信息就没有地方承载,势必会对代码组织形式产生严重的侵入,你看下你现在的代码,一切需要围绕 zod 展开,zod 就是你的爸爸,而且 zod 自己还到处侵入代码组织形式,这就是没有 Decorator 给代码带来的侵入

第三个,不要在 TypeScript 的推导上陷得太深,真实场景下总有它推导不了的情况,要随时允许程序员介入,帮助程序员,而不是教会程序员,做程序员的辅助,而不是程序员的爸爸


我觉得你着实应该好好看看面向对象,咋说,咱不耻下问吧,写几个 fastapi 的 demo ,在语言之间多看看,横向比较,不要把自己局限在 js/ts 生态里面
pseudo
2022-11-20 20:13:06 +08:00
@amlee #3 嗯,看上去是有点类似,之后可以做到通过类型定义 / SQL 直接生成 CRUD 接口。和 Hasura 有点像,但 stick with REST ,其余与框架无关
@debuggerx #4 赞 👍 向你学习
amlee
2022-11-20 20:52:36 +08:00
@pseudo 在 fastapi 里,自动生成 CRUD 接口,用户系统,认证 /鉴权之类的功能,都是在 fastapi 起来之后,社区里面其他开源项目提供的。我体验了下,可以做到无缝集成。

我不知道你该不该自己做这些东西,但如果项目出名了,肯定有人会做。

加油
pseudo
2022-11-20 23:30:04 +08:00
@amlee #6 谢谢,我的创业项目就是做用户系统的: https://logto.io
codehz
2022-11-21 09:41:08 +08:00
没用装饰器是对的,装饰器的问题在于,它没准明年就会被新的草案代替,按照 ts 官方的意思,很可能最早 24 年(假设装饰器 23 年出)就会全面转向新的装饰器草案(毕竟一直说旧版的是 experiment ,不算稳定接口的一部分)
nomagick
2022-11-21 10:20:24 +08:00
@codehz 没那回事,es 装饰器是在 ts 装饰器基础上设计改进的,只是输入输出变了,本质功能是一脉相承的,到时候改一下装饰器实现就可以了。
装饰器这东西又不是 ts 新发明的,它是个啥东西别的语言早就定义好了,es 只需要确定细节

你这就像尤雨溪说 class 的定义不稳定所以不提供 class 写法一样,怎么不稳定了,class 又不是新发明的,它是个啥东西别的语言早就定义好了,八九不离十,最后是啥样根据情况微调一下实现就是了
devtiange
2022-11-21 11:29:24 +08:00
帅! 感谢楼主分享.
codehz
2022-11-21 11:39:39 +08:00
@nomagick 新的设计改了很多语义,比如你不能在装饰器里拿到原型,也不能在类装饰器里拿到属性 /方法装饰器的数据,最重要的是,不能改变成员的属性,你不再能把属性变 getter setter ,也不能增加删除成员
基本上和 ts 的装饰器不一样了,现在 tc39 的态度就是不应该在严肃项目里用现有的装饰器

Babel 7 supports the decorators proposal presented to TC39 in the November 2018 TC39 meeting. It's fine to use these for experimental purposes, but they face significant performance issues, are not yet widely adopted; we don't plan to continue pushing for this proposal in TC39. As such, we recommend against using this version for serious work. In follow-on proposals to add more built-in decorators, we hope to be able to recover the extra functionality that the November 2018 decorators proposal supported.
pseudo
2022-11-21 11:41:09 +08:00
@codehz #8 是的,我很早之前用过装饰器的库( mobx )并尝试过写装饰器,感觉像个黑盒,并且和 FP 的理念不一致,如果要使用就得一条路走到黑。加上一直处于不稳定状态,之后就完全不用了。
@devtiange #9 谢谢支持
nomagick
2022-11-21 12:50:57 +08:00
@codehz 类装饰器还是可以拿到 prototype , 你说的没法直接拿到 prototype 仅限于其它类型的装饰器,换句话说只是不再允许胡乱魔改 prototype ,这个没什么的。因为可以使用闭包或者时序机制把其他装饰器的信息收集到类装饰器一起做修改。

在这个帖子语境底下 Decorator 主要是用来承载一些 metadata, 这是 Decorator 或者说 Annotation 最基本的用法。
如果连这种用法都不支持,那它就不叫 Decorator 了,TC39 也变成一个失能委员会,分分钟被几大巨头另立中央。

“不应该在严肃项目里用现有的装饰器”这个我没见过,如果这样那它还搞这些麻烦事干啥啊,直接拒了提案不就得了。
jchnxu
2022-11-23 17:36:53 +08:00
楼主加油,曾经有过类似的想法。。也觉得 koa 那种 mutable 的方式很不好管理
jchnxu
2022-11-25 00:30:26 +08:00
而且我也在想,zod 这种库,理论上也可以搞到 orm 里面去,production 可以不启用,development 其实挺有用的,尤其你要存 json 的时候

这样能从 db 一口气通到客户端
pseudo
2022-11-25 17:05:52 +08:00
@jchnxu #13 或者直接用 zod 之类的库当作类型定义的 SSOT 是不是也是个办法?个人比较喜欢 native + fp 的路子,代码比较清晰。ORM 加了一层抽象会屏蔽数据库的一些特性,有利有弊吧。
jchnxu
2022-11-25 22:13:35 +08:00
我其实也是 native + fp 的拥趸,和你的意思是一样的



我 sequelize 大概这么写了一下是能跑通的。ZodColumn 给 get & set 都加一个 validate 。

比较难的是
1. 怎么去掉那个 z.infer ,这个类型体操我有点做不动 FYI https://stackoverflow.com/a/61683916/1922857
2. 怎么把这个 User 只能弄成一个 z.object ,那就可以做类似于
pseudo
2022-11-27 01:23:57 +08:00
@jchnxu 不太熟悉 class + decorator ,简单看了一下代码感觉 class prop 本身类型定义可能无法避免,可能可以从 decorator 下手。还有 class level decorator 可能也可以尝试。
感觉主要原因还是两种不同的对数据模型处理的方向,所以并不能很顺利地结合。我们项目里 SSOT 是 SQL ,zod 定义都是生成的,维护起来稍微简单点。
yetrun
2023-08-07 09:49:00 +08:00
这篇主题的行文方式很值得我学习。但这篇主题表达的内容我尚不明白。Mark!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/896571

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX