如何使用Hono和Zod构建类型安全的API

如何使用Hono和Zod构建类型安全的API

💡 原文英文,约3700词,阅读约需14分钟。
📝

内容提要

本文介绍如何使用Hono和Zod构建类型安全的Node.js API,通过单一Zod模式同时处理运行时验证、TypeScript类型和OpenAPI文档,避免三者漂移。文章涵盖项目设置、API模式定义、数据库与HTTP模式分离、路由契约、精简处理器、统一错误格式、自动生成文档及生产环境优化,并展示这些模式在ClipForge视频处理工具中的扩展应用。

🔎

延伸解读

单一数据源的价值

文章强调,通过将运行时验证、TypeScript类型和OpenAPI文档统一到一个Zod模式中,可以避免三者之间的漂移。这种设计减少了维护成本,因为修改模式时,类型和文档会自动更新,无需手动同步。对于团队协作或长期维护的项目,这能显著降低因不一致导致的bug。

数据库模式与HTTP模式的分离

作者建议将数据库模式与HTTP模式分开,因为两者关注点不同:数据库模式反映持久化结构,HTTP模式定义公开契约。这种分离允许内部字段(如`archivedAt`)不暴露给客户端,同时服务层负责映射。这有助于保持API稳定,并适应数据库结构的演进。

统一错误处理的重要性

文章展示了如何通过`ApiError`类统一所有错误响应格式,包括验证错误、业务错误和未知错误。这确保了客户端始终收到一致的结构,便于错误处理。此外,路由中显式声明可能的状态码,增强了API的可预测性和文档准确性。

模式在大型项目中的扩展

通过ClipForge案例,作者展示了这些模式如何扩展到多服务架构。共享Zod模式在API、worker和前端之间保持一致,路由仍作为契约层,而数据库模式保持独立。这证明了小项目中的良好实践可以平滑地应用于复杂系统,无需改变核心哲学。

Q&A

什么是Hono和Zod?它们如何帮助构建类型安全的API?

Hono是一个基于Web标准API的小型快速Web框架,支持Node.js、Bun、Deno和边缘运行时。Zod是一个TypeScript优先的schema验证库,可以同时提供运行时验证、TypeScript类型推断和OpenAPI文档生成。通过@hono/zod-openapi结合使用,可以用一个Zod schema同时处理运行时验证、TypeScript类型和OpenAPI文档,避免三者漂移。

如何用Zod定义一个schema,使其同时用于运行时验证、TypeScript类型和OpenAPI文档?

使用@hono/zod-openapi中的z对象定义schema,并调用.openapi()方法添加OpenAPI元数据。例如:const taskSchema = z.object({...}).openapi('Task')。然后通过z.infer<typeof taskSchema>获取TypeScript类型,该schema会自动用于运行时验证和OpenAPI文档生成。

为什么需要将数据库schema和API schema分开?

数据库schema和API schema代表不同的边界:数据库schema描述数据在数据库中的存储结构,而API schema定义客户端可见的HTTP契约。在复杂应用中,两者可能不同,例如数据库有内部字段(如archivedAt)而API不暴露。分开可以避免耦合,使内部变更不影响外部契约,并允许服务层进行映射。

在Hono中如何定义路由契约,并保持处理器精简?

使用createRoute定义路由契约,包括方法、路径、请求和响应schema。处理器只负责调用服务并返回结果,不进行手动解析或业务逻辑。通过c.req.valid('param')或c.req.valid('json')获取已验证和类型化的数据。这样路由定义成为文档,处理器保持精简。

如何统一API的错误响应格式?

通过ApiError类封装所有错误,使用ApiError.parse()将不同类型的错误(如ZodError、自定义错误)转换为统一的ApiError对象,并返回一致的JSON结构。在路由的defaultHook中处理验证错误,确保所有失败路径返回相同的错误形状。

如何自动生成OpenAPI文档,并确保文档与代码同步?

通过Hono的OpenAPI集成,在路由定义中附加schema,然后使用app.doc('/doc', {...})生成OpenAPI JSON,并通过Scalar提供交互式文档。由于文档从路由定义生成,当schema或路由更改时,文档自动更新,无需手动维护。

在生产环境中,除了类型安全,还需要注意哪些方面?

需要验证环境变量(使用Zod)、使用结构化日志(如Pino)、优雅关闭(处理SIGTERM)、保持运行时可移植性(使用Web标准API)。这些措施确保应用在生产环境中的稳定性和可维护性。

这些模式如何扩展到大型应用,如ClipForge?

在ClipForge中,使用共享Zod schema包(packages/shared)在API、worker和web之间共享类型,路由仍定义契约,数据库schema与API schema分离,并应用相同的生产习惯(环境验证、日志、优雅关闭)。这证明了这些模式可以从小型API扩展到多服务系统。

🏷️

标签

➡️

继续阅读