从 v2 迁移至 v5
本文档涵盖从 Keq v2 升级至 v5 的全部破坏性变更,建议按章节顺序逐项处理。
版本说明
keq 核心库此前为 v2,而 keq-cli 已独立发布过 v3 和 v4。为统一所有官方包的版本号,本次发布直接定为 v5。
兼容性要求
Node.js: 最低版本要求从 18 提升至 20。
浏览器:
Chrome | Firefox | Edge | Node.js | |
|---|---|---|---|---|
| 91+ | 90+ | 15+ | 91+ | 20+ |
破坏性变更
1. 包名与导入方式
Named export 替代 default export:
import request from "keq" // ❌ v2: default export
import { request } from "keq" // ✅ v5: named exportrequest 不再是可调用函数,而是 KeqRequest 实例:
request("http://example.com") // ❌ v2
request.fetch("http://example.com") // ✅ v5官方包迁移至 @keq-request scope:
| v2 | v5 |
|---|---|
keq-cache | @keq-request/cache |
keq-headers | @keq-request/headers |
keq-cli | @keq-request/cli |
keq-url | @keq-request/url |
keq-exception | @keq-request/exception |
RequestException 可直接从 keq 导入:
import { RequestException } from "keq-exception" // ❌ v2
import { RequestException } from "keq" // ✅ v52. 路由参数语法
v5 采用 RFC 6570 URI Template 语法,不再支持 Express 风格的 :param:
request.get("/cats/:name/:age") // ❌ v2
request.get("/cats/{name}/{age}") // ✅ v53. Query 参数序列化
数组序列化的默认格式从 brackets 变更为 indices(与 qs 库默认行为一致)。若后端依赖 brackets 格式,需显式指定:
// v2 默认序列化为 ?a[]=1&a[]=2
request.get("/api").query({ a: [1, 2] })
// v5 默认序列化为 ?a[0]=1&a[1]=2,需显式指定以保持兼容
request.get("/api").query({ a: [1, 2] }, { arrayFormat: "brackets" }) v5 支持的格式:
| 格式 | 示例 |
|---|---|
indices(v5 默认) | ?ids[0]=1&ids[1]=2 |
brackets(v2 默认) | ?ids[]=1&ids[]=2 |
repeat | ?ids=1&ids=2 |
comma | ?ids=1,2 |
4. 路径参数编码
v5 中 .params(key, value) 不再自动对 value 进行 URI 编码。包含特殊字符的路径参数需手动编码:
request
.get("/files/{path}")
.params("path", encodeURIComponent("a/b/c"))5. Options API
resolveWithResponse 已移除,使用 resolveWith 替代:
const response = await request
.get("/data")
.option("resolveWithResponse", true) // ❌ v2
.resolveWith("response") // ✅ v5retry 选项合并为单一对象:
await request
.get("/data")
.option("retryTimes", 3) // ❌ v2
.option("retryDelay", 1000) // ❌ v2
.option("retryOn", () => true) // ❌ v2
.option("retry", { // ✅ v5
times: 3,
delay: 1000,
on: () => true,
}) .retry(times, delay, on) 链式方法在 v5 中保持不变,无需修改。
6. 中间件 API
自定义中间件涉及以下变更:
import { KeqMiddleware } from 'keq'
function myMiddleware(): KeqMiddleware {
return async (context, next) => {
// context.metadata 已移除,使用 context.orchestration 替代
context.metadata // ❌ v2
context.orchestration // ✅ v5
// abort 方法移至 context.request
context.abort() // ❌ v2
context.request.abort() // ✅ v5
// routeParams 重命名为 pathParameters
context.request.routeParams // ❌ v2
context.request.pathParameters // ✅ v5
// retry 选项结构变更
context.options.retryTimes // ❌ v2
context.options.retryDelay // ❌ v2
context.options.retryOn // ❌ v2
context.options.retry.times // ✅ v5
context.options.retry.delay // ✅ v5
context.options.retry.on // ✅ v5
// resolveWithResponse 替换为 resolveWith
context.options.resolveWithResponse = true // ❌ v2
context.options.resolveWith = "response" // ✅ v5
// fetch 事件拆分为 fetch:before 和 fetch:after
context.emitter.on('fetch', callback) // ❌ v2
context.emitter.on('fetch:before', callback) // ✅ v5
context.emitter.on('fetch:after', callback) // ✅ v5
// context.output 变更为 writeOnly,不可读取
const output = context.output // ❌ v2: 可读写
context.output = value // ✅ v5: 仅可写入
await next()
}
}7. TypeScript 类型
类型重命名:
| v2 | v5 | 备注 |
|---|---|---|
KeqOperations | KeqApiSchema | 已移除,必须修改 |
KeqBaseOperation | KeqDefaultOperation | 已移除,必须修改 |
KeqOptions<T> | KeqMiddlewareOptions<OP> | KeqOptions 仍存在但语义已变更为构造函数选项类型 |
API Schema 定义需添加 index signature:
import { KeqOperations } from "keq" // ❌ v2
import { KeqApiSchema, KeqQueryValue, KeqParamValue } from "keq" // ✅ v5
interface MyApi extends KeqOperations { // ❌ v2
interface MyApi extends KeqApiSchema { // ✅ v5
"/cats": {
get: {
requestParams: {} // ❌ v2
requestQuery: { breed: string } // ❌ v2
requestParams: { // ✅ v5
[key: string]: KeqParamValue
}
requestQuery: {
breed: string
[key: string]: KeqQueryValue
}
requestHeaders: {
Authorization: string
[key: string]: string | number // ✅ v5
}
requestBody: {}
responseBody: { id: string; name: string }
}
}
}默认返回类型变更:
v2 中 request.get() 返回 Keq<any>,v5 变更为 Keq<unknown>。建议显式指定类型:
const body = await request.get<CatResponse>("/cats")8. 异常处理
RequestException 构造函数的第三个参数从布尔值变更为选项对象,且语义相反:
// ❌ v2: 第三个参数为 retry(true = 允许重试)
throw new RequestException(400, "Bad Request", true)
// ✅ v5: 第三个参数为选项对象(fatal = true 表示终止重试)
throw new RequestException(400, "Bad Request", { fatal: false }) 语义反转:v2 的 true(允许重试)等价于 v5 的 { fatal: false }(不终止重试)。旧的布尔签名通过 deprecated overload 暂时保留,但由于语义相反,建议立即修改以避免逻辑错误。
v5 新增的异常类型(均可从 "keq" 导入):
AbortException— 请求被中止TimeoutException— 请求超时MutexException— 互斥流控拒绝TypeException— 类型错误
9. 移除的 API
| 已移除 | 替代方案 |
|---|---|
appendMiddlewares() | .use() 或 .apply().forRoutes() |
prependMiddlewares() | .use() 或 .apply().forRoutes() |
retryMiddleware | 已内置,无需手动引用 |
proxyResponseMiddleware | 已内置,无需手动引用 |
fetchArgumentMiddleware | 已内置,无需手动引用 |
10. CLI 代码生成(@keq-request/cli)
目录结构对比
keq-cli@4 生成结构:
outdir
└── animal-service
├── components
│ └── schemas
│ ├── cat.ts
│ ├── dog.ts
│ └── index.ts
├── types
│ ├── get-cat.ts
│ └── get-dog.ts
├── get-cat.ts
├── get-dog.ts
└── index.ts@keq-request/cli 生成结构:
outdir
├── request.ts
└── animal-service
├── types
│ ├── components
│ │ └── schemas
│ │ ├── cat.schema.ts
│ │ ├── dog.schema.ts
│ │ └── index.ts
│ └── operations
│ ├── get-cat.type.ts
│ └── get-dog.type.ts
└── operations
├── get-cat.fn.ts
├── get-dog.fn.ts
└── index.ts迁移步骤
-
删除旧的
outdir目录。 -
修改
.keqrc.(yml|json|js|ts)配置文件:.keqrc.tsimport { defineKeqConfig, FileNamingStyle } from "keq-cli"; import { defineConfig, FileNamingStyle } from "@keq-request/cli"; import { ChineseToPinyinPlugin, OverwriteAdditionalPropertiesPlugin, OverwriteOperationIdPlugin, OverwriteQueryOptionsPlugin, SpringdocCompatPlugin } from "@keq-request/cli/plugins"; export default defineKeqConfig({ export default defineConfig({ outdir: './outdir', // v5 不再提供默认 request 配置 request: './custom_request_file_path', fileNamingStyle: FileNamingStyle.snakeCase, rendering: { fileNamingStyle: FileNamingStyle.snakeCase, // 兼容模式提供 keq 废弃的 .useRouter.module(...) API 支持 v2Compat: true, // v5 版本默认不生成 index.ts 文件,旧版本默认生成 index.ts entrypoint: true, // v5 版本默认使用更严谨 unknown 类型,旧版本使用 any 类型 additionalPropertiesType: 'any', }, modules: { animalService: 'http://example.com/v1/swagger.json', }, operationIdFactory: ({ operation }) => operation.operationId, plugins: [ // ChineseToPinyinPlugin 在旧版本中默认内置,v5 需显式添加 new ChineseToPinyinPlugin(), // OverwriteAdditionalPropertiesPlugin 在旧版本中默认内置,v5 需显式添加 new OverwriteAdditionalPropertiesPlugin({ disallowIfNotPresent: true, }), // v5 校验更严格,不严格符合规范的文档会报错,添加此插件做兼容性修正 new SpringdocCompatPlugin(), // 代替 operationIdFactory new OverwriteOperationIdPlugin(({ operation }) => operation.operationId), // v5 默认序列化为 indices,需显式指定以保持兼容 new OverwriteQueryOptionsPlugin({ arrayFormat: 'brackets' }), ], }); -
重新生成代码:
- npm
- pnpm
- yarn
-
更新导入路径:
import { getCat } from '@outdir/animal-service' import { getCat } from '@outdir/animal-service/operations' import { request } from 'keq' import { request } from '@outdir/request'
v5 新增功能
完成迁移后,可使用以下新特性(详见各功能文档):
| 功能 | 说明 |
|---|---|
| 生命周期事件 | 通过事件监听请求各阶段(发起、完成、重试、超时、中断、异常),实现日志、埋点等横切关注点 |
| NestJS 集成 | 提供 KeqModule 支持在 NestJS 中以依赖注入方式管理 API 请求与中间件 |
| MCP Server | 内置 MCP 服务,使 AI 编程助手可语义搜索 API 定义并生成类型安全的调用代码 |
| CLI 插件系统 | 基于 tapable 的编译钩子机制,支持自定义代码生成逻辑与输出格式 |



