从头开始创建一个自动产生文档/类型安全的现代API(4) 启用Zod/代码重组

启用Zod

Zod 是一个 TypeScript 优先的模式声明和验证库。我使用术语 “模式” 来广义地指任何数据类型,从简单的 字符串 到复杂的嵌套对象。Zod 围绕尽可能友好的开发体验而设计。其目的是消除重复的类型声明。使用 Zod,你只需声明 一次 验证器,Zod 就会自动推断出静态 TypeScript 类型。将简单类型组合成复杂的数据结构非常容易。我的理解,它补足了 Typescript 在动态类型检查上的缺陷,让程序可以更稳定的运行,减少因为类型不匹配导致的问题。
可以用 bun add zod 安装,但实际上我们之前因为依赖,已经安装过了。

创建Zod Schema

创建文件 utility/env.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { z } from "zod";
import { expand } from 'dotenv-expand';
import { config } from 'dotenv';

expand(config());

const EnvSchema = z.object({
NODE_ENV: z.string().default("development"),
PORT: z.coerce.number().default(9999),
LOG_LEVEL: z.enum(["fatal","error","warn", "info", "debug","trace"]),
});

export type env = z.infer<typeof EnvSchema>;
// eslint-disable-next-line ts/no-redeclare
const { data: env, error } = EnvSchema.safeParse(process.env);

if (error) {
console.error("❌ Invalid env:");
console.error(JSON.stringify(error.flatten().fieldErrors, null, 2));
process.exit(1);
}

export default env;

其他代码中用到 process.env.xxx 都改成 env.xxx

测试Zod

我们可以测试一下Zod的校验功能,在.env 中注释掉LOG_LEVEL:

1
2
3
NODE_ENV=development
PORT=3001
# LOG_LEVEL=debug

运行结果Zod会报错:

1
2
3
4
5
6
❌ Invalid env:
{
"LOG_LEVEL": [
"Required"
]
}

代码重组

将代码分拆到不同文件,降低代码复杂度。

types.ts

将定义都放到 utility/types.ts:

1
2
3
4
5
6
7
8
9
10
import { OpenAPIHono } from "@hono/zod-openapi";
import { PinoLogger } from "hono-pino";

export interface AppBinding {
Variables: {
logger: PinoLogger;
}
};

export type AppOpenAPI = OpenAPIHono<AppBinding>;

create-app.ts

将app定义放到 app/api/[[...route]]/lab/create-app.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { OpenAPIHono } from "@hono/zod-openapi";
import { notFound, onError } from "stoker/middlewares";
import { pnLogger } from "@/middlewares/pino-logger";

import type { AppBinding } from "@/utility/types";

export default function createApp(){
const app = new OpenAPIHono<AppBinding>({
strict: false,
}).basePath('/api');
app.use(pnLogger());

app.notFound(notFound);
app.onError(onError);

return app;
}

修改 route.ts

修改 app/api/[[...route]]/route.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { handle } from 'hono/vercel';

import createApp from "./lab/create-app";

const app = createApp();

app.get("/", (c) => {
return c.text("Hello Hono");
});

app.get("/error", (c) => {
c.status(422);
//add a custom error message to logger
c.var.logger.info("Wow! Log here");
throw new Error("This is an error");
});

export const GET = handle(app);

运行程序:bun run dev,测试一下,结果与之前一致。

作者:Bearalise
出处:从头开始创建一个自动产生文档/类型安全的现代API(4) 启用Zod/代码重组
版权:本文版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明,必须在文章中给出原文链接。

请我喝杯咖啡吧~

支付宝
微信