完全用typescript写了个grpc service框架
发布于 6 个月前 作者 xiaozhongliu 1083 次浏览 来自 分享

proto定义好接口即可一条命令生成controller及引用的类型的定义 (class & interface). 效仿egg的『约定优于配置』原则, config, midware, controller, 相关type定义等只要按约定放到相应的文件夹即可.

框架还未单独封装, 现在放在framework目录下. 代码生成器还未单独封装, 现在放在codegen目录下. 这些现在是我的业余兴趣, 会利用闲余时间阶段性添加功能.😂 https://github.com/xiaozhongliu/ts-rpc-seed

代码生成

ts-node codegen

运行服务

# 本地使用vscode的话直接进F5调试typescript
# 或者:
npm run tsc
node dist/app.js

测试请求

ts-node tester
# 或者:
npm run tsc
node dist/tester.js

代码样例

入口app.ts
import App from './framework'
new App().start()
config
export default (appInfo: AppInfo): Config => {
    return {
        // basic
        PORT: 50051,

        // log
        COMMON_LOG_PATH: `${appInfo.rootPath}/log/common`,
        REQUEST_LOG_PATH: `${appInfo.rootPath}/log/request`,
    }
}
midware
import { Context } from '../framework'
import 'dayjs/locale/zh-cn'
import dayjs from 'dayjs'
dayjs.locale('zh-cn')

export default async (ctx: Context, req: object, next: Function) => {
    const start = dayjs()
    await next()
    const end = dayjs()

    ctx.logger.request({
        '@duration': end.diff(start, 'millisecond'),
        controller: `${ctx.controller}.${ctx.action}`,
        metedata: JSON.stringify(ctx.metadata),
        request: JSON.stringify(req),
        response: JSON.stringify(ctx.response),
    })
}
controller
import { Controller, Context } from '../framework'
import HelloReply from '../typings/greeter/HelloReply'

export default class GreeterController extends Controller {

    async sayHello(ctx: Context, req: HelloRequest): Promise<HelloReply> {
        return new HelloReply(
            `Hello ${req.name}`,
        )
    }

    async sayGoodbye(ctx: Context, req: HelloRequest): Promise<HelloReply> {
        return new HelloReply(
            `Goodbye ${req.name}`,
        )
    }
}

请求日志输出类似

image.png

下一项功能

把在这里用的参数校验中间件搬过来, 用class-validator和class-transformer实现这样的效果, 并大部分自动生成:

import { IsOptional, Length, Min, Max, IsBoolean } from 'class-validator'

export default class IndexRequest {
    @Length(4, 8)
    @IsOptional()
    foo: string

    @Min(5)
    @Max(10)
    @IsOptional()
    bar: number

    @IsBoolean()
    @IsOptional()
    baz: boolean
}
8 回复

debug with ts-node directly vscode -> F5 -> tsnode

是 vscode -> Ctrl+Shift+d -> F5 么

@waitingsong 我平时是直接F5的,默认快捷键,你说的组合键好像是调试中重启。 友情提醒ts-node调试只有6.x支持。

@zuohuadong 今天把坑填完了, 框架封装好了, 后面更新一下说明再发出来. 我想会给大家带来惊喜的. 写框架和用框架包括查看类型定义, 都是ts源码哦, 基本不使用type definition file, 用框架完全感受不到type definition file.

上面的示例应用已经通过npm包使用这个框架了, 剩下个小问题, 我准备搞, 如果有大佬知道怎么搞请告知: tsc的时候node_modules里的框架ts文件会报不影响运行的错误, 如何禁用. 比如: image.png 我的tsconfig:

{
    "compileOnSave": true,
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2018",
        "charset": "utf8",
        "noImplicitAny": true,
        "esModuleInterop": true,
        "resolveJsonModule": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "forceConsistentCasingInFileNames": true,
        "allowSyntheticDefaultImports": true,
        "noImplicitUseStrict": true,
        "listEmittedFiles": true,
        "removeComments": true,
        "skipLibCheck": true,
        "sourceMap": false,
        "pretty": true,
        "baseUrl": ".",
        "paths": {
            "*": [
                "node_modules/*",
                "typings/*",
            ],
        },
        "outDir": "dist",
    },
    "exclude": [
        "node_moddules",
        "codegen",
    ],
}

我记得 grpc 也有个纯js 的实现,不过只有 client ~ 这个要是能有 grpc 官方维护就牛皮了~

@zuohuadong 这配置没大差异, 是正常的. 找到原因了. tsc --listFiles分析下来就只有我这个npm包是直接走index.ts获取类型的, 其他都是.d.ts或者.js nest那边是框架内部直接export / import definition的, 并不是动态搜索typings目录这种.

回到顶部