Postman to OpenAPI Converter
【免费下载链接】scalarScalar is an open-source API platform: 🌐 Modern REST API Client 📖 Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar
一、核心导读
本篇文章将以@scalar/postman-to-openapi包为核心,带你掌握如何将 Postman Collection(含v2.1等版本)一键转换为标准的 OpenAPI 3.1 文档。文章将从安装、基础用法、输入检测、高级选项到源码级的转换原理进行逐步拆解,帮助你:
- 快速将现有的 Postman 集合转换成 OpenAPI 文档,接入 Scalar API Reference、SDK 生成器等生态;
- 深入理解转换器如何解析请求、路径、认证、请求体、响应与服务器,并利用
mergeOperation、requestIndexPaths、tagNamingStrategy等选项进行精细控制; - 通过源码与实际 fixture 了解其内部实现,避免黑盒使用。
二、什么是@scalar/postman-to-openapi
@scalar/postman-to-openapi是 Scalar 开源 API 平台提供的一个 TypeScript 包,用于把 Postman Collection(Postman Collection Format)转换为开放标准 OpenAPI(Swagger)文档。它基于社区项目 joolfe/postman-to-openapi 改进而来,并在其基础上适配了 Scalar 生态,因此也被视为现代继任者。
该包的定位可以从其package.json的description中看出:"Converts Postman collections to OpenAPI documents"(package.json)。它不依赖任何运行时 GUI,只负责数据格式转换,因此非常适合集成到 CI、构建脚本、CLI 工具或编辑器插件中。
从 packages/postman-to-openapi/src/index.ts 可以看出,包对外导出的核心 API 只有三个:
convertisPostmanCollectionextractPathFromUrl/normalizePath(URL 工具函数)
这也体现了它的设计哲学:轻量、无副作用、聚焦于转换本身。
三、安装与快速开始
3.1 安装
该包发布在 npm 上,包名为@scalar/postman-to-openapi:
npm install @scalar/postman-to-openapi3.2 最小示例
import { convert } from '@scalar/postman-to-openapi' // Free the postman! const result = await convert(myPostmanCollection) console.log(result)convert接受一个Postman Collection 对象或Postman Collection 的 JSON 字符串。从源码 src/convert.ts 中的parseCollectionInput可以看到,当传入字符串时,内部会调用JSON.parse进行解析;若解析失败,会抛出名为PostmanCollectionParseError的异常,错误信息为Invalid Postman collection JSON: <details>。
3.3 返回值:OpenAPI 3.1 文档
convert返回的是符合OpenAPI 3.1.0规范的文档对象(OpenAPIV3_1.Document)。在源码中可以看到,当不传入基础文档时,转换器会初始化一个最小文档:
const openapi: OpenAPIV3_1.Document = baseDocument ?? { openapi: '3.1.0', info: { title, version, ...(description && { description }), ...(license && { license }), ...(contact && { contact }), ...(logo && { 'x-logo': logo }), }, paths: {}, }也就是说,info.title取自集合的info.name(缺省为'API'),info.version优先从集合的variable中寻找 key 为version的变量(缺省为'1.0.0'),info.description、license、contact、x-logo等则分别从集合中提取。
小技巧:如果希望控制生成文档的版本号,可以在 Postman 集合的 Collection Variables 中定义一个名为
version的变量,例如1.2.0。
四、检测输入是否为 Postman Collection
在实际的 CLI 或集成场景中,输入往往是不确定来源的文本。此时可以使用isPostmanCollection来判断:
import { convert, isPostmanCollection } from '@scalar/postman-to-openapi' if (isPostmanCollection(input)) { const openApiDocument = convert(input) console.log(openApiDocument) }4.1 判断逻辑
isPostmanCollection接受一个JSON 字符串,其判断逻辑在 src/is-postman-collection.ts 中:
- 尝试
JSON.parse,失败则返回false; - 解析结果必须是对象且不能是数组;
- 集合必须包含一个Postman schema 地址(
info.schema的 host 必须是schema.getpostman.com); - 满足以下任意一个条件即可:
- 包含
info._postman_id; - 包含一个顶层
item数组。
- 包含
这意味着:导出的集合即使缺少info._postman_id,只要包含合法的 Postman schema URL 和item树,也能被正确识别。这一点非常重要,因为部分 Postman 导出选项并不会生成_postman_id。
4.2 输入校验
当调用convert时,还会执行更严格的形状校验(validateCollectionShape),包括:
- 必须是对象;
- 必须包含
info; item必须是数组;info.name必填;info.schema必填;- 如果提供了
variable,则必须是数组。
这些校验能帮助你在转换前尽早发现格式问题,而不是在转换中途才报错。
五、核心转换流程(源码级拆解)
5.1 整体流程
convert的完整处理流程可以概括为(源码见 src/convert.ts):
- 解析输入:
parseCollectionInput(对象直接使用,字符串则JSON.parse); - 校验形状:
validateCollectionShape; - 提取文档级信息:title / version / description / license / contact / logo;
- 处理 externalDocs;
- 处理认证:如果集合带有
auth,调用processAuth生成securitySchemes与security,并合并进文档; - 遍历
item树:逐个调用processItem生成 paths / components / serverUsage,并通过mergePathItem合并; - 路径参数统一:
unifyEquivalentPathParameters将等价但参数名不同的路径合并; - 服务器放置:
analyzeServerDistribution根据服务器使用频率决定把servers放到文档级、路径级还是操作级; - 清理:
cleanupOperations删除空parameters、空描述以及内部 bookkeeping 扩展字段;最后pruneDocument修剪空字段并返回。
5.2 item 树遍历:processItem
processItem(src/helpers/path-items.ts)是整个转换的核心,它递归处理Item | ItemGroup:
- 如果是ItemGroup(包含
item数组),则递归遍历子项,并把组名累积到parentTags中; - 如果是Item(包含
request),则:- 解析请求 URL,拆出path与server;
- 把路径参数从 Postman 的
:param形式归一化为 OpenAPI 的{param}形式; - 提取
operationId(请求名中[xxx]方括号内的内容)、summary、description; - 根据文件夹层级生成tags;
- 提取 query / path / header参数(同时支持从请求描述中的 Markdown 参数表格解析参数);
- 处理请求级
auth(生成securitySchemes与security); - 处理请求体(支持 raw / urlencoded / formdata / file / graphql 等模式);
- 提取responses(含保存的响应、状态码解析);
- 处理pre-request / post-response 脚本,生成
x-pre-request、x-post-response扩展字段。
5.3 请求名中的状态码约定
一个非常有用的约定:如果请求的名称以200 - 描述或200: 描述的形式开头,parseStatusCodeFromRequestName会将其解析为响应的状态码和描述(src/helpers/path-items.ts 中的parseStatusCodeFromRequestName)。例如:
- 请求名
200 - OK→ 生成responses['200'] = { description: 'OK' }; - 请求名
404: Not Found→ 生成responses['404'] = { description: 'Not Found' }。
这让你可以在不改动响应数据的情况下,仅通过命名规范就让转换结果包含有意义的状态码描述。
5.4 请求体模式支持
在 src/helpers/request-body.ts 中,转换器支持以下 Postman body 模式:
| Postman body 模式 | 转换结果 |
|---|---|
raw(JSON / XML / text 等) | requestBody.content对应媒体类型与 schema |
urlencoded | application/x-www-form-urlencoded |
formdata | multipart/form-data |
file | 文件上传类型的媒体类型 |
graphql | GraphQL 请求体(含 query / variables) |
同时,ensureRequestBodyContent会保证requestBody.content不为空,缺省时回退为text/plain。值得注意的是,即使请求方法是 GET,只要存在 body,转换器也会保留requestBody(见 src/helpers/path-items.ts 中的注释 "Allow request bodies for all methods (including GET)")。
5.5 认证转换:processAuth
processAuth(src/helpers/auth.ts)负责把 Postman 的认证配置转换为 OpenAPI 的securitySchemes与security。它支持的类型包括(对应 src/types.ts 中Auth的定义):
basic(HTTP Basic)bearerapikeyoauth1/oauth2digestawsv4(AWS Signature V4)edgegrid(Akamai EdgeGrid)hawk、ntlm等
转换发生在两个层级:
- 集合级 auth(
collection.auth):生成文档级components.securitySchemes与security; - 请求级 auth(
request.auth):生成操作级security,并合并securitySchemes到components。
两者可以并存,从而实现"全局默认认证 + 单请求覆盖"的效果。
5.6 响应提取:extractResponses
src/helpers/responses.ts 负责从 Postman 保存的响应(response[])中提取 OpenAPI 的responses对象。每个保存的响应会映射为一个状态码,响应体与 Content-Type 会被转换为对应的媒体类型与示例。当没有保存响应时,会使用默认描述(DEFAULT_RESPONSE_DESCRIPTIONS)。
同时,请求头中的Accept会被用于推断响应的媒体类型(pickAcceptMediaType),这是让转换结果更精确的关键细节之一。
六、convert的高级选项
convert的第二个参数是一个ConvertOptions对象。其完整定义在 src/convert.ts 中:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mergeOperation | boolean | false | 相同 path + method 的操作是否合并为一个 operation。为true时,多次出现的同名操作会合并,并通过generateUniqueValue生成唯一的示例名(如Default example#1) |
requestIndexPaths | readonly PostmanRequestIndexPath[] | 未定义(转换整个集合) | 只转换指定索引路径上的请求。每个路径是从collection.item出发的零基索引数组,最后一个索引可以指向请求或文件夹(文件夹会包含其全部后代请求) |
tagNamingStrategy | 'leaf' \| 'chain' | 'leaf' | 从嵌套文件夹生成 OpenAPI tag 的策略。leaf只使用文件夹名,重名时回退为父级 / 叶子;chain保留完整的文件夹链,用>连接 |
document | OpenAPIV3_1.Document | 未定义 | 已有的 OpenAPI 文档,转换结果会合并进该文档(见下节) |
keepHeaders | readonly string[] | [] | 需要保留为parameters[in=header]的请求头 key(不区分大小写)。默认会过滤掉传输层、内容协商与认证相关头(Accept、Content-Type、Authorization、Host等) |
6.1mergeOperation的底层原理
当mergeOperation: true时,mergePathItem(src/helpers/merge-path-item.ts)会调用mergeOperations把同一路径同一方法的多个 operation 合并。为了避免示例名冲突,它会:
- 收集目标 path 上已有的示例名;
- 用
generateUniqueValue生成不冲突的新示例名(例如Default example#1); - 通过
renameOperationExamples重命名新加入操作的示例; - 同时更新
x-postman-pre-request-scripts/x-postman-post-response-scripts等扩展字段的 key。
6.2requestIndexPaths:按需转换子集
对于大型集合,你可能只想转换其中部分请求。requestIndexPaths允许你通过零基索引路径精确选择:
import { convert } from '@scalar/postman-to-openapi' // 只转换 collection.item[0].item[2](文件夹或请求)下的所有请求 const result = convert(collection, { requestIndexPaths: [[0, 2]], })从源码看,[0, 2, 1]表示collection.item[0].item[2].item[1]。越界或穿过非文件夹节点的路径会被跳过;同时 tag 只从被选中的路径的祖先文件夹中提取(extractTagContextsForSelectedPaths),避免引入无关 tag。
6.3tagNamingStrategy:控制 tag 生成
Postman 集合通常用嵌套文件夹组织请求。转换器默认使用leaf策略——只用最内层文件夹名作为 tag,例如Parent > Child > Leaf只生成 tagLeaf;如果不同父目录下存在同名叶子文件夹,会回退为Parent / Leaf以避免冲突。而chain策略则保留完整链,生成 tagParent > Child > Leaf。
如果你希望 API 文档按完整目录树组织,选chain;如果希望更简洁,用默认的leaf即可。
6.4document:合并进已有 OpenAPI 文档
这是一个非常实用的特性:你可以把 Postman 转换的结果合并进一份已有的 OpenAPI 文档。合并规则(见 src/convert.ts):
- 根级
info与已有 paths 会被保留(除非 Postman 侧新增或合并了操作); tags按名称取并集;securitySchemes与servers会去重合并;- 已有文档的
externalDocs优先保留。
在 src/convert.test.ts 中有对应测试('merges into an existing OpenAPI document'),验证了合并后info.title仍为已有文档的'Existing'、/health路径保留、新增的/v2/echo被加入、tagCore保留。
6.5keepHeaders:保留特殊请求头
默认情况下,转换器会过滤掉一些"传输层 / 内容协商 / 认证"头,避免它们在 OpenAPI 中变成无意义的parameters[in=header]。但如果你有一个 API 故意使用这些头名(例如自定义的Authorization语义),可以通过keepHeaders显式保留:
const result = convert(collection, { keepHeaders: ['Authorization', 'X-Custom-Accept'], })七、服务器(servers)的智能放置
Postman 集合中的每个请求都有自己的 URL。转换器会为每个请求提取 server 对象(extractServerObjectFromUrl),并记录每个 server 在哪些 path / method 上被使用(ServerUsage)。
随后,analyzeServerDistribution(src/helpers/servers.ts)会根据使用频率决定 servers 的放置层级:
- 所有或多数 path 都在用→ 放到文档级
openapi.servers; - 一个 path 内的多个操作在用→ 放到路径级
pathItem.servers; - 只有一个操作在用→ 放到操作级
operation.servers。
这种"就近放置"策略让生成的 OpenAPI 文档更符合规范推荐的覆盖语义,也避免在文档级堆砌一堆只在个别接口使用的 server。
此外,src/helpers/urls.ts 中的createCollectionVariableLookup会建立集合变量的查找表,因此在请求 URL 中出现的{{baseUrl}}等变量也会被解析到对应的 server 值中。
八、路径参数统一(Path Unification)
一个常见痛点是:同一资源在不同请求里使用了不同的路径参数名(如/users/{id}与/users/{userId})。转换器通过unifyEquivalentPathParameters(src/convert.ts)自动处理:
- 对每个 path 计算"结构签名"(
getPathStructuralSignature),忽略参数名只保留结构; - 结构相同的 path 归为一组;
- 优先采用文件夹模板提示(如果某个文件夹名本身是
/applications/{id}这样的模板,会从中提取参数名);否则取出现次数最多的参数名(chooseMostCommonName); - 将所有同组 path 重写为规范参数名,并合并到一条规范 path 上。
这样最终输出的 OpenAPI 文档不会出现结构相同但参数名各异的冗余路径。
九、实际 fixture 一览
仓库在 packages/postman-to-openapi/fixtures/input 下提供了 26 个覆盖不同场景的输入 fixture(JSON),并被 src/convert.test.ts 以快照测试的方式逐一验证。它们是学习转换规则的最佳样例:
| Fixture | 覆盖的转换场景 |
|---|---|
AuthBasic.json/AuthBearer.json/AuthMultiple.json/AuthRequest.json | 集合级与请求级的认证转换 |
DeleteOperation.json | DELETE 操作与响应 |
EmptyUrl.json/NoPath.json | URL 缺失或路径缺失时的兜底行为 |
ExternalDocs.json | externalDocs提取 |
Folders.json | 嵌套文件夹与 tag 生成 |
FormData.json/FormUrlencoded.json | multipart/form-data与application/x-www-form-urlencoded请求体 |
GetMethods.json/SimplePost.json | 基础 GET / POST 转换 |
Headers.json | 请求头到parameters[in=header]的转换与过滤 |
LicenseContact.json/XLogo.json | license、contact与x-logo提取 |
MultipleServers.json/NestedServers.json | 多 server 的放置策略 |
NoVersion.json | 缺少版本号时回退1.0.0 |
OperationIds.json | 请求名中[operationId]的解析 |
ParseStatusCode.json | 请求名中200 - xxx状态码解析 |
PathParams.json | :param到{param}的归一化 |
RawBody.json | raw 请求体转换 |
Responses.json/ResponsesEmpty.json | 保存响应的提取与空响应兜底 |
UrlWithPort.json | 带端口的 URL 处理 |
每个 fixture 对应的快照输出位于 src/snapshots/convert.test.ts.snap,你可以对照输入与快照,直观理解每种 Postman 结构会生成怎样的 OpenAPI。
十、测试与验证方式
该包使用 Vitest 作为测试框架(见 package.json 中的"test": "vitest --run")。你可以通过以下命令运行全部测试:
pnpm test测试覆盖包括:
- 26 个 fixture 的快照测试;
convert的合并、tag 生成、参数处理等行为测试;isPostmanCollection的识别测试;- 各 helper(auth、contact、external-docs、form-data、license、logo、markdown、parameters、responses、servers、status-codes、urls 等)的单元测试。
这些测试即是最好的"文档",它们精确说明了每个输入结构会得到怎样的输出。
十一、在 Scalar 生态中的位置
@scalar/postman-to-openapi是 Scalar API 平台数据导入能力的一环。转换得到的 OpenAPI 3.1 文档可以直接用于:
- ScalarAPI References(交互式 API 文档);
- ScalarAPI Client(离线优先的 API 客户端);
- ScalarSDK Generator(生成 TypeScript / Python / Go / PHP / Java / Ruby 的类型安全 SDK);
- 任何符合 OpenAPI 规范的生态工具。
这意味着:把 Postman 集合"解放"(Free the postman)为标准 OpenAPI 文档后,整个开放生态都向你敞开,这正是该包的核心价值主张。
十二、小结
| 能力 | 说明 |
|---|---|
| 输入 | Postman Collection 对象或 JSON 字符串(v2.x格式) |
| 输出 | OpenAPI 3.1 文档对象 |
| 认证 | basic / bearer / apikey / oauth / digest / awsv4 等 |
| 请求体 | raw / urlencoded / formdata / file / graphql |
| 响应 | 保存响应提取 + 请求名状态码约定 + Accept 推断媒体类型 |
| 高级选项 | mergeOperation/requestIndexPaths/tagNamingStrategy/document/keepHeaders |
| 智能处理 | 服务器就近放置、路径参数统一、tag 去重、内部扩展字段清理 |
使用@scalar/postman-to-openapi,你可以把组织内沉淀在 Postman 中的接口资产,一键沉淀为开放、标准、可被任意 OpenAPI 工具链消费的规范文档,实现"一次转换、处处可用"。
参考实现与测试:src/convert.ts · src/index.ts · src/is-postman-collection.ts · src/helpers/path-items.ts · src/helpers/servers.ts · src/convert.test.ts · fixtures/input
【免费下载链接】scalarScalar is an open-source API platform: 🌐 Modern REST API Client 📖 Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考