V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
pseudo
V2EX  ›  分享创造

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

  •  1
     
  •   pseudo · 2022-11-20 14:48:58 +08:00 · 2895 次点击
    这是一个创建于 576 天前的主题,其中的信息可能已经有所发展或是发生改变。

    契机

    • 之前关注到 trpc 这个项目,觉得类型推导的方式非常天才,但它并不支持 RESTful APIs
    • 喜欢 KoaJS 的理念,但它需要 mutate context (ctx.body = {}),同时感觉社区活跃度在下降
    • 不管是 KosJS 还是 ExpressJS 都是纯 JavaScript 开发,实际使用中与 TypeScript 结合总有别扭之处
    • TypeScript 日益强大,并且原生 fetch 等功能逐渐稳定并普及

    结果

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

    https://github.com/withtyped/withtyped

    效果如图:

    Banner

    一些亮点

    • 还是 RESTful API ,熟悉的配方
    • 用任意工具 guard 请求输入 (path params, query, body) 和输出 (response)
    • 写 API 送强类型客户端
    • 零依赖,原生就好
    • 自动生成 OpenAPI (Swagger) 接口

    细节

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

    Screenshot 2022-11-20 at 11.43.38.png

    Screenshot 2022-11-20 at 12.23.35.png

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

    Screenshot 2022-11-20 at 12.24.42.png

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

    Screenshot 2022-11-20 at 11.37.29.png

    Screenshot 2022-11-20 at 11.38.53.png

    Screenshot 2022-11-20 at 11.39.25.png

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

    router.withOpenApi()
    

    结尾

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

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

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

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

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

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


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

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

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

    你这就像尤雨溪说 class 的定义不稳定所以不提供 class 写法一样,怎么不稳定了,class 又不是新发明的,它是个啥东西别的语言早就定义好了,八九不离十,最后是啥样根据情况微调一下实现就是了
    devtiange
        11
    devtiange  
       2022-11-21 11:29:24 +08:00
    帅! 感谢楼主分享.
    codehz
        12
    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
        13
    pseudo  
    OP
       2022-11-21 11:41:09 +08:00
    @codehz #8 是的,我很早之前用过装饰器的库( mobx )并尝试过写装饰器,感觉像个黑盒,并且和 FP 的理念不一致,如果要使用就得一条路走到黑。加上一直处于不稳定状态,之后就完全不用了。
    @devtiange #9 谢谢支持
    nomagick
        14
    nomagick  
       2022-11-21 12:50:57 +08:00
    @codehz 类装饰器还是可以拿到 prototype , 你说的没法直接拿到 prototype 仅限于其它类型的装饰器,换句话说只是不再允许胡乱魔改 prototype ,这个没什么的。因为可以使用闭包或者时序机制把其他装饰器的信息收集到类装饰器一起做修改。

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

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

    这样能从 db 一口气通到客户端
    pseudo
        17
    pseudo  
    OP
       2022-11-25 17:05:52 +08:00
    @jchnxu #13 或者直接用 zod 之类的库当作类型定义的 SSOT 是不是也是个办法?个人比较喜欢 native + fp 的路子,代码比较清晰。ORM 加了一层抽象会屏蔽数据库的一些特性,有利有弊吧。
    jchnxu
        18
    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
        19
    pseudo  
    OP
       2022-11-27 01:23:57 +08:00 via iPhone   ❤️ 1
    @jchnxu 不太熟悉 class + decorator ,简单看了一下代码感觉 class prop 本身类型定义可能无法避免,可能可以从 decorator 下手。还有 class level decorator 可能也可以尝试。
    感觉主要原因还是两种不同的对数据模型处理的方向,所以并不能很顺利地结合。我们项目里 SSOT 是 SQL ,zod 定义都是生成的,维护起来稍微简单点。
    yetrun
        20
    yetrun  
       316 天前
    这篇主题的行文方式很值得我学习。但这篇主题表达的内容我尚不明白。Mark!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5647 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 01:46 · PVG 09:46 · LAX 18:46 · JFK 21:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.