Adapter 架构
VextJS 使用 Adapter 架构替换底层 HTTP 处理层。基于 VextJS req / res 编写的路由与服务通常可以保持原有接口;底层框架专属的中间件、插件和能力仍需核对适配边界。本页先完成一次安装、配置、启动和切换验证,再解释实现自定义适配器需要满足的接口。
内置 Adapter
VextJS 内置 5 种 Adapter,覆盖主流 Node.js HTTP 框架:
以上范围来自当前 VextJS 包的依赖声明;升级时以实际安装版本的 peer 要求为准。选中其他 Adapter 不会自动开放该框架的原生插件注册接口。
性能对比
本页不保存独立数字快照。公开基准固定同一个轻量 Vext Normal 应用,只更换五个受支持的 Adapter;它比较 Vext Adapter 的集成路径,不是各底层框架的独立 Raw 性能或 Vext 相对 Raw 的百分比。
请在性能基准查看保留的结果、测试方法、限制和复现命令。目前公开样本来自 2026-08-15、Vext 1.0.1 的指定提交,不是当前 2.0.0 的性能基线。选择 adapter 后,仍应使用实际中间件、认证、日志和 I/O 负载验证你的目标。
使用方法
先跑通一条路由
前置条件:已按快速开始准备 Node.js、ESM package.json、TypeScript 配置及 dev/build/start scripts。以下在 API-only 项目中验证,不需要数据库或外部服务;已有项目请合并配置,并避开同名路由。
文件名 adapter-demo.ts 会生成 /adapter-demo 前缀,文件内只写 /:id 和 /,避免重复前缀。
运行 npm run dev,在另一个终端执行(PowerShell 使用 curl.exe):
预期分别为:200 且 data 为 { "id": "u-1", "adapter": "native" };201 且 data.name 为 Alice;422 校验错误;404 未匹配路由。成功响应默认还有 code: 0 与 requestId。
先用 Ctrl+C 停止 dev,再执行 npm run build 和 npm start,重复四个请求,验证生产产物。结束后停止服务释放端口。下方各配置示例只展示 Adapter 选项,合并到当前配置,保留其他字段。
Native Adapter(默认)
不需要安装额外依赖,也不需要显式配置——默认即为 Native Adapter:
如需显式声明:
Native Adapter 使用 Node.js 原生 http.createServer 处理 HTTP,配合 route-core 做路由匹配;它是默认路径,且不依赖第三方 HTTP 框架。实际性能取决于场景,请以当前基准和你的业务压测为准。
Hono Adapter
推荐方式(字符串标识):
高级用法(工厂函数):
Hono 是一个超轻量级的 Web 框架,基于 Web Standards API(Request / Response)。当前内置 Hono Adapter 是 Node.js HTTP server adapter,运行时只依赖 hono;用于接入 Hono 路由的 node:http 请求/响应桥接由 Vext 自己实现。@hono/node-server 不是该 Adapter 的运行时依赖。
这不等同于官方 Edge / Serverless Adapter 支持。Cloudflare Workers、Deno Deploy、Bun edge 或其他非 Node.js 运行时需要单独的 Edge / Serverless 适配器或生态插件支持,不能直接把当前 vextjs/adapters/hono 当作 Edge 运行时保证。
Fastify Adapter
推荐方式(字符串标识):
高级用法(工厂函数,可传入选项):
VextJS 使用 Fastify 承载路由与 HTTP 服务,校验和 JSON 序列化由 VextJS 自己的链路处理。res.json() 先经 VextJS 序列化后发送,不会因选择 Fastify 就自动改用 Fastify 的 route schema 或插件。需要设置选项时使用工厂,例如 fastifyAdapter({ caseSensitive: true });传入的是 FastifyAdapterOptions,不是任意 Fastify 配置。
Express Adapter
推荐方式(字符串标识):
高级用法(工厂函数,可传入选项):
当前实现基于 Express v5。迁移时可复用与 HTTP 对象无关的业务逻辑;原 Express 路由与 (req, res, next) 中间件需要按 VextJS 接口调整。工厂的 ExpressAdapterOptions 提供 bodyLimit 字符串选项,不等于接受整个 Express 应用实例。
现有 Express v4 依赖不满足本 Adapter 的 peer 范围。请先核对应用依赖与迁移影响,再安装符合要求的版本;不能仅更换 adapter 字符串就认为迁移完成。
Koa Adapter
推荐方式(字符串标识):
高级用法(工厂函数,可传入选项):
当前实现基于 Koa v3,由 @koa/router 负责路由匹配,两个包都需要安装。KoaAdapterOptions 提供 bodyLimit 字符串选项;VextJS 中间件接收统一请求/响应对象,不接收 Koa ctx。
切换 Adapter
先停止当前服务,安装目标 peer 包(Hono 为 npm install hono),再修改 src/config/default.ts:
重新执行 npm run dev 与四个请求:成功响应中的 data.adapter 应变为 hono,状态码、参数和校验结果保持一致。停止 dev 后重新 build/start 再验证,避免生产仍运行旧产物。其余 Adapter 按依赖表安装并更换字符串,用同样步骤核验。
基于 VextRequest / VextResponse 的路由 handler 和服务代码通常可以复用。迁移还应覆盖项目实际使用的大小写/尾斜杠、查询参数、上传、流式响应、取消和错误路径;四个入门请求不能证明整个业务迁移已完成。
如何选择 Adapter
选择 Native(默认推荐)
- 希望从框架默认路径开始
- 不需要其他 HTTP 框架的特定能力
- 新项目,没有 adapter 迁移约束
- 希望减少额外依赖
选择 Hono
- 团队了解 Hono,希望采用其路由与 Web Request/Response 桥接实现
- 部署目标是受支持的 Node.js 环境
- 已验证所需功能可通过 VextJS 公共接口使用;原生 Hono 中间件另行适配
选择 Fastify
- 需要使用 Fastify 的路由实现或本 Adapter 暴露的选项
- 团队已有 Fastify 运维和排障经验
- 已验证业务负载;不要把 Fastify 原生插件或自动序列化当成切换后自动获得的能力
选择 Express
- 从现有 Express 项目迁移到 VextJS
- 愿意将原生 HTTP 中间件转换为 VextJS 中间件
- 团队对 Express 最熟悉
选择 Koa
- 团队已有 Koa 与
@koa/router的使用经验 - 接受安装两个 peer,并已经验证路由匹配行为
- 已为需要的原生 Koa 中间件安排适配
工作原理
Adapter 负责:
- 启动 HTTP 服务 — 使用底层框架创建服务器并监听端口
- 请求转换 — 将底层框架的原生请求对象转换为
VextRequest - 响应转换 — 将
VextResponse的操作映射到底层框架的响应对象 - 路由注册 — 将框架收集到的路由注册到底层路由系统
- 中间件执行 — 收集全局中间件,在路由执行时按 VextJS 约定组合执行链
VextAdapter 接口
所有 Adapter 实现统一的 VextAdapter 接口:
OpenAPI / Docs 路由由框架通过 registerRoute() 注册,Adapter 不再提供单独的 registerOpenAPIRoutes()。
自定义 Adapter
配置支持内置名称、同步工厂 (app: VextApp) => VextAdapter,或已构造的 VextAdapter 对象。工厂会在应用初始化时获得当前 app;解析器只检查名称和必需方法是否存在,不会替你证明中间件、错误处理或关闭语义正确。
如果只需在现有实现外增加逻辑,可以先组合内置 Adapter。下面是可运行的委托示例,保留 Native 的全部行为,只增加名称标识;它不代表已经实现另一套 HTTP 框架:
将原配置的 adapter 字段改为该工厂,保留其他字段:
重复上方 dev/build/start 与四个请求,成功响应中的 data.adapter 应为 my-custom。真正接入另一种 HTTP 实现时,需要自行实现以下契约,不能把注册方法留空或让所有请求固定返回 501:
- 将请求转换为
VextRequest,补齐路由模板、参数、原始正文读取与生命周期信号;将响应转换为框架需要的VextResponse。 - 保留全局中间件与路由 chain 的顺序、
await next()回程、错误和 404 处理,以及传入的RouteOptions。 buildHandler()返回 Node.js 请求处理函数且不监听端口,供 dev handler 替换使用;listen()处理监听失败、server 选项并返回实际端口与可等待的close()。- 以真实 HTTP 验证正常/错误/校验、头与 Cookie、上传/流/断连以及关闭;测试开发和生产两条启动路径。前端渲染由框架安装,不能凭接口形状就宣布支持所有前端能力。
请求/响应转换
无论使用哪种 Adapter,业务代码优先操作统一接口。下面是主要成员摘要,省略了完整泛型及内部响应钩子;准确的公共调用签名和行为见请求与响应。不要把摘要复制成自定义 Adapter 的完整实现。
VextRequest(统一请求对象)
_getRawBody() / _getRawBodyBuffer() 由 Adapter 注入,主要供框架中间件和 multipart 等插件使用;普通业务代码优先使用 req.body、req.files 和 req.valid()。
VextResponse(统一响应对象)
stream() / download() 接收 Node.js Readable / NodeJS.ReadableStream,不是 Web ReadableStream。rawJson() 以及下划线开头的响应方法是框架内部接口,业务代码应使用 VextPublicResponse 可见的公共方法。
这种设计意味着:
- 公共接口为路由与中间件提供复用基础。
- 每个 Adapter 都要实现框架的请求、响应与中间件契约;原生框架的对象和接口不属于该契约。
- 纯业务单元测试通常可复用,HTTP 集成测试仍应在实际选用的 Adapter 上执行。
按环境切换 Adapter
配置加载器支持按环境覆盖 Adapter。只有明确需要并分别验证过两种实现时才这样设置;通常开发与生产保持同一 Adapter 更便于复现问题。下面仅展示覆盖机制,需提前安装 Hono:
常见问题
切换 Adapter 后需要修改代码吗?
只使用公共接口的代码通常可以复用。读取原生对象、依赖框架专属插件或路由细节的代码需要调整,并在目标 Adapter 上回归;不能承诺所有业务代码都无需修改。
可以在运行时动态切换 Adapter 吗?
不可以。Adapter 在启动时由配置决定,运行时不可切换。如需根据环境使用不同 Adapter,请使用配置文件覆盖机制(如 development.ts / production.ts)。
性能差异主要来自哪里?
性能差异来自底层框架的 HTTP 解析、路由匹配、序列化,以及 Vext 与各 adapter 的集成路径。公开历史样本只比较相同 Vext Normal 负载下的 Adapter 差异,没有提供各自 Raw 基线的百分比;某场景领先不代表所有场景都领先。请结合性能基准的版本与口径,用你的实际中间件和 I/O 负载复测。
底层框架的原生中间件能用吗?
不能直接作为 VextJS 中间件传入。defineMiddleware / defineMiddlewareFactory 使用统一请求、响应与 next,签名及生命周期与原生框架不同。可独立于 HTTP 对象的逻辑可以封装进 VextJS 中间件或插件;依赖原生实例的扩展需要实现桥接或自定义 Adapter,仅套一层函数不能保证兼容。
peer dependencies 报警告怎么办?
这里的可选表示未选用该 Adapter 时无需安装;选用后相应 peer 必须存在且兼容。Hono 需要 hono,Koa 同时需要 koa 与 @koa/router。请区分未使用的可选包、实际选中包缺失和版本不兼容,不要统一忽略安装警告。
当前 Hono Adapter 是 Node.js 运行时能力:它通过 Node.js HTTP server 接收请求,并把请求桥接给 Hono 的 Web Request / Response 处理流程。Edge / Serverless 运行时不应使用这组 Node adapter 安装说明作为支持声明。
启动或切换失败如何定位?
Cluster 源 IP 亲和性与自定义 Adapter
五种内置 Adapter 在 cluster.sticky: "ip" 下共用 Node HTTP 接收层,保留各自的路由、请求处理与关闭生命周期。Fastify 通过其正式 serverFactory 接入,仍执行 ready/listen/close。
公开类型 VextAdapterFactory 的签名是 (app, context?: VextAdapterRuntimeContext) => VextAdapter。第二参数是每个实例的运行上下文;只有实际启用亲和性的 Cluster Worker 才传入 context.socketHandoff,其中 host/port 是 Master 已绑定的公共地址。自定义工厂不能仅因项目配置含 sticky 就自行切换运行模式。
自定义 Adapter 默认仍只需实现原有接口。支持该模式时,应设置 supportsSocketHandoff: true,消费工厂上下文,并让 listen() 返回带 receiveSocket(socket) 和同步 forceClose() 的 VextServerHandle。此时 Worker 不绑定公共或私有 TCP 端口;receiveSocket 接收已提交的暂停 socket,登记所有权后才恢复读取。close 等待在途连接,forceClose 释放全部持有的连接(包括 upgrade),并与框架 shutdown 的绝对期限配合。缺少能力时在监听前报错;宣称支持却缺少返回控制方法时启动失败。
旧的一参数工厂在普通模式下继续可用。封装内置工厂时需要把上下文一起传递,例如 (app, context) => nativeAdapter()(app, context)。仅将 buildHandler() 注入未监听 server 无法保证 Node 超时、连接统计和关闭合同,不能作为完整的 handoff 实现。
下一步
- 了解 配置 中 Adapter 相关的配置项
- 查看 OpenAPI 文档 在不同 Adapter 下的表现
- 探索 Cluster 多进程 与 Adapter 的配合
- 阅读 性能基准 相关的基准测试数据