概览
Keq 提供的命令行工具可以将 swagger 文档编译为 typescript 代码。从而能像调用函数一样发送 HTTP 请求。
安装
- npm
- pnpm
- yarn
建议在 package.json 锁定 @keq-request/cli 的版本。
@keq-request/cli 的小版本升级为了修复 Bug 可能会修改代码模板。这有极小的概率导致代码不向前兼容。
使用方法
首先,我们需要一份 swagger 文档,例如下面的 cat-service-swagger.json 文件,描述了一个获取猫咪信息的 HTTP 接口。
cat-service-swagger.json
{
"openapi": "3.0.0",
"info": {
"title": "Cat Service",
"version": "0.0.1"
},
"paths": {
"/cats": {
"get": {
"operationId": "getCats",
"parameters": [
{
"name": "breed",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Cat"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Cat": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
}
}
}
}
}然后,我们创建 @keq-request/cli 的配置文件,指定 swagger 文档的位置和编译参数:
- Typescript
- Javascript
- YAML
import { defineKeqConfig, FileNamingStyle } from "@keq-request/cli"
export default defineKeqConfig({
outdir: "./src/apis", // 编译结果的输出目录
rendering: {
fileNamingStyle: FileNamingStyle.snakeCase,
},
modules: {
catService: "./cat-service-swagger.json",
// 也可以从网络上获取 swagger 文档,例如:
// dogService: "http://dog.example.com/swagger.json"
},
})const { defineKeqConfig, FileNamingStyle } = require("@keq-request/cli")
module.exports = defineKeqConfig({
outdir: "./src/apis", // 编译结果的输出目录
rendering: {
fileNamingStyle: FileNamingStyle.snakeCase,
},
modules: {
catService: "./cat-service-swagger.json",
// 也可以从网络上获取 swagger 文档,例如:
// dogService: "http://dog.example.com/swagger.json"
},
})outdir: ./src/apis # 编译结果的输出目录
rendering:
fileNamingStyle: snake_case
modules:
catService: ./cat-service-swagger.json
# 也可以从网络上获取 swagger 文档,例如:
# dogService: http://dog.example.com/swagger.json优先使用 typescript 或 javascript 格式的配置文件。一些高级功能(例如 operationIdFactory)只能通过代码实现。
接下来,在终端执行以下命令:
- npm
- pnpm
- yarn
执行完成后,@keq-request/cli 会在 ./src/apis/cat_service/ 目录下生成如下文件:
- 请求函数
- 接口类型定义
- 数据模型定义
import { Keq } from "keq"
import { request } from '../../request'
import type { GetCatsOperation, GetCatsResponseBodies, GetCatsRequestParameters } from "../types/operations/get_cats.type.js"
export type { GetCatsRequestQuery, GetCatsRequestHeaders, GetCatsRequestBodies } from "../types/operations/get_cats.type.js"
const moduleName = "catService"
const method = "get"
const pathname = "/cats"
export function getCats<STATUS extends keyof GetCatsResponseBodies, CONTENT_TYPE extends never = never>(args?: GetCatsRequestParameters): Keq<GetCatsOperation<STATUS, CONTENT_TYPE>> {
const req = request.post<GetCatsResponseBodies[STATUS]>("/cats")
.option('module', { name: moduleName, pathname, method })
if (args && "breed" in args) req.query("breed", args["breed"], {"arrayFormat":"repeat"})
return req
}
getCats.pathname = pathname
getCats.method = methodimport type { KeqOperation, KeqPathParameterInit, KeqQueryInit, ServerSentEvent } from "keq"
import type { Cat as CatSchema } from "../components/schemas/cat.schema.js"
export interface GetCatsResponseBodies {
200: CatSchema[] | CatSchema[]
}
export interface GetCatsRequestBodies {}
export type GetCatsRequestQuery = {
"breed"?: string
}
export type GetCatsRouteParameters = {}
export type GetCatsRequestHeaders = {}
export type GetCatsRequestParameters = GetCatsRequestQuery & GetCatsRouteParameters & GetCatsRequestHeaders
export interface GetCatsOperation<STATUS extends keyof GetCatsResponseBodies, CONTENT_TYPE extends string > extends KeqOperation {
requestParams: GetCatsRouteParameters & { [key: string]: KeqPathParameterInit }
requestQuery: GetCatsRequestQuery & { [key: string]: KeqQueryInit }
requestHeaders: GetCatsRequestHeaders & { [key: string]: string | number }
requestBody: object | BodyInit
responseBody: GetCatsResponseBodies[STATUS]
}export interface Cat {
"name"?: string
"age"?: number
}这样,我们就可以调用 getCats 函数来发送 HTTP 请求:
import { getCats } from "./src/apis/cat_service/operations"
const cats = await getCats({ breed: "siamese", unknownKey: 'value' }) // unknownKey 未在 swagger 定义,将被丢弃
.retry(3, 1000)
.timeout(1000)
// 追加额外的请求参数
.query('extraQuery', 'extra')
.set('Authorization', 'TOKEN')
.send({ extraBody: 'extra' })
// 实际请求地址: /cats?breed=siamese&extraQuery=extra
console.log(`我的猫咪有:${cats.map(cat => cat.name).join('、')}`)
console.log(`请求路径:${getCats.pathname}`)BodyFallbackPlugin 可以将 unknownKey 这样的未定义请求参数,全部添加到请求体中。最后,我们可以为 catService 模块的所有接口添加统一的错误处理逻辑:
import { request } from './api/request'
import { throwException, RequestException } from 'keq-exception'
request
.useRouter()
.module('catService', throwException(context => {
if (context.response) {
if (context.response.status >= 400 && context.response.status < 500) {
throw new RequestException(context.response.status, context.response.statusText, false) // 客户端错误,不需要重试
} else if (context.response.status >= 500) {
throw new RequestException(context.response.status, context.response.statusText)
}
}
}))Keq 采用 链式调用 + 中间件 的设计模式?Keq 最初就是为了从 swagger 文档生成请求函数而设计的。然而,只依赖cli 生成代码,许多问题是无法解决的:
swagger文档中定义的 HTTP 接口信息可能是不完整的,甚至是错误的,例如缺少认证信息、缓存策略等参数。 可偏偏一些外部因素阻止我们通过修改swagger文档来补全这些信息。- 客户端在不同场景下调用同一个 HTTP 接口时,可能会有不同的需求,例如在某些场景下需要重试,而在另一些场景下则不需要。
静态生成的代码无法满足这些动态变化的需求。 - 同一个
swagger文档的接口往往有着类似的鉴权、错误处理、日志记录等复杂的需求。这些高度定制化的功能如果使用cli的Plugin生成会非常难以维护。
Keq 便是为给予静态生成代码提供更强大的运行时 API 而设计的 HTTP 客户端:
- 链式调用 让我们在调用
请求函数时,可以动态的修改请求参数,甚至设置违背swagger定义的参数。并且它完美地避免了臃肿的配置选项。 - 中间件 则提供了一种优雅的方式来为同一模块的所有接口添加统一的功能。并可以通过 链式调用 在不同场景下灵活的启用或禁用 中间件。
配置文件
.keqrc.(yml|json|js|ts)
@keq-request/cli 会自动查找名为 .keqrc.yml、.keqrc.json、.keqrc.js、.keqrc.ts的配置文件。
你可以通过 -c --config <config_file_path> 设置配置文件地址。
| 配置参数 | 是否必填 | 默认值 | 描述 |
|---|---|---|---|
| outdir | true | - | 编译结果的输出目录 |
| modules | true | - | Swagger 文件地址和模块名称 |
| rendering | false | {} | 控制代码生成结果的渲染选项 |
| rendering.fileNamingStyle | false | - | 文件名风格 |
| rendering.esm | false | 若 package.json 是否设置了 "type": "module" 则为 true | 是否生成 ESM 风格的代码 |
| rendering.additionalPropertiesType | false | 'unknown' | 控制 additionalProperties: true(或未声明时)在生成的 TypeScript 类型中渲染为 unknown 还是 any |
| rendering.entrypoint | false | false | 开启后会为每个模块生成 index.ts 入口文件,统一 re-export 该目录下的所有生成文件 |
| translators | false | [] | 生成的代码风格 |
| plugins | false | [] | 插件向第三方开发者提供了完整的自定义 cli 行为的能力 |
rendering.fileNamingStyle
| 枚举 | 示例 |
|---|---|
FileNamingStyle.camelCase | "twoWords" |
FileNamingStyle.capitalCase | "Two Words" |
FileNamingStyle.constantCase | "TWO_WORDS" |
FileNamingStyle.dotCase | "two.words" |
FileNamingStyle.headerCase | "Tow-Words" |
FileNamingStyle.noCase | "two words" |
FileNamingStyle.paramCase | "two-words" |
FileNamingStyle.pascalCase | "TwoWords" |
FileNamingStyle.pathCase | "two/words" |
FileNamingStyle.sentenceCase | "Two words" |
FileNamingStyle.snakeCase | "two_words" |
.keqfilter
还可以通过 .keqfilter 文件控制哪些 HTTP 接口需要生成代码。文件使用 INI Section 格式,[deny] 块声明需要忽略的接口,[allow] 块声明例外规则(优先级高于 [deny])。
每行格式为 METHOD module:/pathname,支持 glob 通配符。
[deny]
# 忽略所有模块的所有接口
* *:/**
# 忽略所有模块的 GET /cats 接口
* *:/cats
# 忽略所有模块的 GET 接口
GET *:/**
# 忽略 catService 模块的 /cats 接口
* catService:/cats
# 忽略 catService 模块的 GET /cats 接口
GET catService:/cats
[allow]
# 不忽略 dogService 模块的 POST /dogs 接口
POST dogService:/dogs命令
keq build [options]
按照配置文件的内容,生成代码。
| 选项 | 描述 |
|---|---|
--module <moduleName...> | 仅生成匹配模块名称的模块 |
-c --config <config_file_path> | 配置文件的地址 |
-i --interactive | 通过命令行交互,指定需要生成的 HTTP 接口 |
--tolerant | 容错模式:忽略下载失败和 Swagger 格式校验错误,尽可能生成代码。未设置时,任何错误都会导致构建失败(exit 1) |
--fresh | 构建前清空输出目录,适用于全量重新生成 |
--method <method...> | 仅生成匹配 method('get' | 'post' | 'put' | 'patch' | 'head' | 'options' | 'delete') 的 HTTP 接口 |
--pathname <pathname...> | 仅生成匹配 pathname 的 HTTP 接口 |
keq filter <mode> [options]
添加过滤规则到 .keqfilter 文件。使用 --interactive 可以通过命令行交互指定需要过滤的 HTTP 接口。
| 选项 | 描述 |
|---|---|
<mode> | deny:添加忽略规则;allow:添加例外规则(优先级高于 deny);all:忽略所有接口 |
-c --config <config_file_path> | 配置文件的地址 |
--module | 设置添加的过滤规则的模块名称 |
--method <method> | 设置添加的过滤规则的 HTTP 方法('get' | 'post' | 'put' | 'patch' | 'head' | 'options' | 'delete') |
--pathname <pathname...> | 设置添加的过滤规则的 HTTP 接口路径 |
-i --interactive | 通过命令行交互,指定需要过滤的 HTTP 接口 |
--tolerant | 容错模式:忽略下载失败和 Swagger 格式校验错误 |
--build | 过滤规则添加完成后,自动执行 keq build 命令生成代码 |
keq mock [options]
根据 OpenAPI 文档启动一个 Mock HTTP 服务器,自动生成符合 Schema 定义的模拟数据。适用于前端开发阶段在后端接口尚未就绪时进行联调。
| 选项 | 描述 |
|---|---|
-c --config <config_file_path> | 配置文件的地址 |
-p --port <port> | 监听端口(默认:3939) |
--host <host> | 绑定的主机地址(默认:localhost) |
--module <moduleName...> | 仅 mock 匹配模块名称的模块 |
--cors | 启用 CORS 响应头(默认:true) |
--delay <delay> | 响应延迟(毫秒),支持范围格式如 "100-500" |
--max-depth <depth> | Mock 数据生成时的最大 Schema 深度(默认:10) |
--ref-depth <depth> | 循环 $ref 引用的最大递归深度(默认:5) |
--debug | 打印调试信息 |
--tolerant | 容错模式:忽略无效的 Swagger/OpenAPI 文档结构 |
Mock 数据生成优先级
Mock 服务器按以下优先级生成响应数据:
- OpenAPI Media Type 中定义的
example字段 examples中的第一个可用条目- 基于 Schema 使用 json-schema-faker 自动生成
keq search <query> [options]
使用自然语言语义搜索 API 接口。基于 intfloat/multilingual-e5-small 嵌入模型,支持中英文跨语言匹配。
| 选项 | 描述 |
|---|---|
<query> | 自然语言搜索查询(必填) |
-c --config <config_file_path> | 配置文件的地址 |
--module <moduleName...> | 仅在匹配模块名称的模块中搜索 |
--limit <limit> | 最大返回结果数量(默认:10) |
--detail | 在结果中包含完整的请求/响应 Schema(parameters、requestBody、responses) |
--format <format> | 输出格式,可选值:json、compact(默认:json) |
--all | 忽略 .keqfilter 规则,搜索所有接口 |
--tolerant | 容错模式:忽略无效的 Swagger/OpenAPI 文档结构 |
--debug | 打印调试信息 |
首次运行时会自动下载嵌入模型到 ~/.cache/keq/models 目录,后续运行将直接使用缓存。
输出示例
compact 格式:
[0.92] GET /cats 获取所有猫咪信息 (catService#getCats)
[0.85] POST /cats 创建一只新猫咪 (catService#createCat)
json 格式:
{
"results": [
{
"score": 0.92,
"module": "catService",
"method": "GET",
"pathname": "/cats",
"operationId": "getCats",
"summary": "获取所有猫咪信息",
"description": "",
"tags": ["cats"]
}
],
"total": 1
}keq apis [options]
列出 OpenAPI 文档中定义的所有 API 接口和组件信息,包括接口的 summary/description 和 schema 的 description。
| 选项 | 描述 |
|---|---|
-c --config <config_file_path> | 配置文件的地址 |
--includes <includes...> | 指定输出内容,可选值:operations、components |
--module <moduleName...> | 仅列出匹配模块名称的模块 |
--method <method> | 仅列出匹配 HTTP 方法的接口 |
--pathname <pathname> | 仅列出匹配路径的接口 |
--format <format> | 输出格式,可选值:compact、json |
--json | 以 JSON 格式输出(等同于 --format json) |
--all | 忽略 .keqfilter 规则,列出所有模块和接口。不可与 --module、--method、--pathname 同时使用 |
--tolerant | 容错模式:忽略无效的 Swagger/OpenAPI 文档结构 |
--debug | 打印调试信息 |
输出示例
Module: catService
──────────────────────────────────────────────────────────────────────────
APIs:
GET /cats
获取所有猫咪信息
POST /cats
创建一只新猫咪
Schemas:
Cat
猫咪数据模型