
从 Postman 集合到 OpenAPI 文档scalar/postman-to-openapi 转换引擎的演进与实战指南【免费下载链接】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/scalarscalar/postman-to-openapi是 Scalar 开源 API 平台中负责把 Postman Collection 转换为 OpenAPI 3.1 文档的核心包服务于用 OpenAPI 统一管理 API 描述、摆脱对单一供应商锁定的导入链路。本文以该包的 CHANGELOG 为骨架结合 源码 与测试系统梳理convert/isPostmanCollection的完整用法、全部配置项以及 0.1.0 → 0.7.19 之间的关键能力演进帮助你理解转换器内部做了什么、为什么这样做以及如何在项目里正确使用它完成 Postman → OpenAPI 的迁移。包定位与快速上手在 Scalar 的包体系中postman-to-openapi位于packages/postman-to-openapi/其定位在 README 中写得很直白把 Postman 集合转换成开放标准 OpenAPI让使用者从 Postman 的私有格式中解放出来Free the postman!。它继承并现代化改造了社区同名项目postman-to-openapi包名保持了一致。安装需要 Node.js 22见 package.json 的engines字段npm install scalar/postman-to-openapi基本用法import { convert } from scalar/postman-to-openapi const result convert(myPostmanCollection) // 或直接传入集合的 JSON 字符串 console.log(result)需要说明的是convert本身是同步函数类型签名为(postmanCollection, options?) OpenAPIV3_1.Document见 convert.tsREADME 示例里的await只是无害的演示写法。包导出的公开 API 集中在 index.tsconvert/ConvertOptions/TagNamingStrategy核心转换函数与配置isPostmanCollection集合识别extractPathFromUrl/normalizePathURL 与路径的工具函数其中normalizePath会把:id风格的路径参数统一改写为{id}见 urls.ts。转换管线的整体流程convert的执行流程对应 convert.ts可以概括为七个阶段理解这条管线有助于后续章节的内容定位输入解析与校验字符串输入先JSON.parse解析失败抛出PostmanCollectionParseError随后校验info.name、info.schema、item数组等必要字段缺失即报错convert.ts。文档骨架初始化从info.name取标题缺省API从集合变量version取版本缺省1.0.0并处理 license、contact、logox-logo、externalDocs 等元数据。认证处理集合级auth与请求级auth都会被转换为 OpenAPI 的securitySchemes与security。逐条转换请求遍历item树把每个请求转换为 OpenAPI operation——提取 URL 中的路径与服务器、路径参数、请求头、请求体、响应与脚本。路径合并与参数统一将各请求产出的 path item 合入paths并把结构等价但参数名不同的路径合并为一条规范路径。服务器分层放置基于服务器使用频率决定servers放在文档级、路径级还是操作级。清理与剪枝删除内部簿记扩展字段与空对象输出整洁的 OpenAPI 3.1.0 文档。convert 的完整配置项ConvertOptions是控制转换行为的唯一入口全部字段及语义如下见 convert.ts 的类型注释配置项类型默认值作用mergeOperationbooleanfalse是否合并同路径同方法的多个 Postman 请求为一个 OpenAPI operation。false时后出现的请求会覆盖先前请求true时进入深度合并示例、参数、脚本按来源保留requestIndexPathsreadonly number[][]未设置全量转换只转换指定索引路径下的请求。索引从collection.item逐层下钻最后一项可指向请求或文件夹文件夹包含全部后代请求越界或经过非文件夹的路径会被跳过tagNamingStrategyleaf \| chainleafOpenAPI tag 命名策略leaf只用最末层文件夹名重名时回退为父 / 子chain保留连接的全链路documentOpenAPIV3_1.Document无新建文档传入既有 OpenAPI 文档进行增量合并Postman 路径并入tag 按名称取并集securitySchemes 与 servers 合并既有info与旧路径保留keepHeadersreadonly string[][]即使命中内置的传输/内容协商/认证头黑名单如Accept、Content-Type、Authorization、Host等仍将这些请求头保留为parameters[inheader]。仅当 API 刻意以非常规方式使用这些头名时才需要按索引路径筛选请求是批量导入场景的利器。以下示例只转换collection.item[0].item[0].item[0]这一个请求对应测试 convert.test.tsconst nestedOnly convert(collection, { requestIndexPaths: [[0, 0, 0]] }) // 输出 paths 仅含该请求对应路径且保留该分支的文件夹 tag 上下文合并进既有文档的用法同一测试文件中的merges into an existing OpenAPI document用例const result convert(collection, { document: baseOpenApiDocument, // 既有 openapi: 3.1.0 文档 }) // result.info.title 保持为既有文档的 Existing/health 等旧路径被保留标签命名策略leaf 默认值与 chain 回退Postman 的文件夹天然形成层级OpenAPI 的tags却是扁平的如何把文件夹层级映射为 tag 名是转换器最早需要回答的问题。0.7.0 之前默认用拼接完整文件夹链chain 风格0.7.0 起改为leaf 优先策略PR #8900leaf默认tag 名只取最末层文件夹名并自动附带Part of 父链这样的上下文描述如Parent - Child层级会生成{ name: Child, description: Part of Parent }同名叶子重名时先回退为父 / 子仍重名则回退为完整链convert.ts。chain保留旧行为tag 名为父 子 孙的完整链条用于兼容既有消费方。文件夹描述为空字符串时上下文描述会接管避免生成无描述的 tag见测试 convert.test.ts。文件夹名若本身是 URL 模板如/languages/{languageCode}normalizeLeafTagSegment会将其提炼为可读的叶子标识如languageCode避免 tag 名里出现{/}和长路径convert.ts。const result convert(collection, { tagNamingStrategy: leaf }) // 默认 const legacy convert(collection, { tagNamingStrategy: chain }) // 兼容旧版行为服务器 URL 与集合变量解析Postman 集合几乎总是用{{baseUrl}}、{{host}}这类模板变量书写 URL。0.7.0 引入的服务器变量解析PR #8899是这条链路上最重要的改进核心实现在 urls.tscreateCollectionVariableLookup把collection.variable构建为查找表跳过disabled与无值变量urls.ts。extractServerObjectFromUrl解析 URL 中的{{...}}模板变量值可查且为完整 URL如https://api.example.com时直接替换变量值可查且为普通字符串如api.example.com时拼进服务器 URL变量不可解析或值本身又含模板语法递归变量时保留{变量名}占位并在 OpenAPIservers[].variables中生成默认值为example.com、描述为 Declared in Postman collection variables. 的变量定义保证输出文档依然合法且可读urls.ts。// Postman 集合 { variable: [{ key: baseUrl, value: https://api.example.com }], item: [{ name: getUsers, request: {{baseUrl}}/users }] }// 转换结果server 部分 { servers: [{ url: https://api.example.com }] }服务器最终放哪一层由analyzeServerDistribution决定servers.ts覆盖全部路径 → 文档级覆盖多条路径 → 文档级同路径内多个操作 → 路径级仅单个操作 → 操作级。这让输出在少重复和表达准确之间取得平衡。无响应体状态码与响应描述0.7.0 默认对无响应体状态码1xx、204、205、304省略content字段PR #8895。实现在 responses.ts 的hasNoResponseBodyStatusCode如果 Postman 保存的响应恰好带 body会打印警告但仍保留 content避免数据丢失。响应描述采用状态码感知的默认值0.6.3PR #8897内置DEFAULT_RESPONSE_DESCRIPTIONS映射200 → OK、201 → Created、404 → Not found、500 → Internal server error 等responses.ts。当 Postman 响应命名为200 - 用户列表这类状态码 - 描述格式时extractDescriptionFromName会解析出命名示例并用作响应描述responses.ts。媒体类型的选择优先级为保存响应自身的Content-Type→ 请求Accept头pickAcceptMediaType优先application/json→application/json兜底。响应体示例还会通过inferSchemaFromExample反推 schema让生成的 OpenAPI 带上有实际值的示例与结构。另一个值得一提的细节是从测试脚本提取状态码extractStatusCodesFromTests会解析pm.response.to.have.status(201)、pm.expect(pm.response.code).to.eql(202)、pm.expect(pm.response.status).to.equal(201)三种常见断言模式把测试中期望的状态码补充到响应定义里status-codes.ts。操作合并与请求变体保留Postman 中同一个路径方法常常存在多个请求变体例如创建用户 - 成功、创建用户 - 参数错误而 OpenAPI 的paths不允许重复的路径方法键。0.6.0 引入mergeOperation支持重复操作PR #85110.6.3 又进一步打磨了合并行为PR #8902请求级示例保留每个变体的参数示例、请求体示例按来源名保留重名示例自动生成唯一名generateUniqueValue以#后缀去重状态码响应合并从请求名推导出的状态码响应取并集而不是后写覆盖脚本拼接pre-request / post-response 脚本按来源名分区存储最终渲染为以// --- 来源名 ---分隔的拼接文本merge-operation.ts。合并的底层实现在mergeOperations参数按name/in键去重并浅合并examplesrequestBody.content按媒体类型合并 schema 与示例responses取并集summary 取更短者description 以空行拼接去重后的内容merge-operation.ts。// 让同路径同方法的多个 Postman 请求合并为一个 operation并保留各自的示例与脚本 const result convert(collection, { mergeOperation: true })路径参数统一结构等价路径合并Postman 集合里经常出现/users/{id}、/users/:userId、/users/{{uid}}这样仅参数名不同的结构等价路径——它们在 OpenAPI 里会变成两条不同的 key。0.6.3 的unifyEquivalentPathParametersPR #8898专门解决这个问题convert.tsgetPathStructuralSignature把参数段统一替换为{*}得到结构签名urls.ts签名相同的路径聚为一组参数名取出现次数最多者作为规范名chooseMostCommonName若文件夹名本身是路径模板如GET /applications文件夹对应/applications/{id}优先用文件夹模板里的参数名作为规范名其余路径与操作中的 path 参数统一改名然后mergePathItem合并进规范路径。该特性保证同一资源的不同写法最终落到唯一路径 key 上参数定义与路径模板保持一致避免半条路径的碎片化。脚本导入pre-request 与 post-responsePostman 的事件脚本预请求脚本与测试脚本是很多工作流的关键。0.2.0 起支持导入 post-response 脚本PR 018e8b2转换结果写入 OpenAPI 扩展字段预请求脚本listen: prerequest→x-pre-requestpre-request-scripts.ts测试脚本listen: test→x-post-responsepost-response-scripts.ts。内部还会以x-postman-example-name、x-postman-pre-request-scripts、x-postman-post-response-scripts、x-postman-folder-segments等x-扩展携带来源信息以支撑合并但在最终输出前cleanupOperations会把这些内部簿记字段全部剥离确保最终 OpenAPI 文档干净convert.ts。Postman 集合识别与输入健壮性isPostmanCollection用于在转换前判断输入是否为 Postman 集合0.6.3 起从本包共享给其他消费方PR #8903。识别规则is-postman-collection.ts值得注意导出的集合并不保证包含info._postman_id因此只要满足以下条件即判定为 Postman 集合schema URL 的 host 为schema.getpostman.com且存在info._postman_id或存在item数组。import { convert, isPostmanCollection } from scalar/postman-to-openapi if (isPostmanCollection(input)) { const openApiDocument convert(input) console.log(openApiDocument) }输入健壮性还包括非 JSON 字符串会抛出带PostmanCollectionParseError名称的错误Invalid Postman collection JSON: ...缺info/item/info.schema等关键结构会立即报错而非静默产出残缺文档convert.ts0.6.3 还修复了非法 JSON body 的处理PR #8887。0.2.0 则把/raw与/等特殊路径做了专门处理并移除了默认 tag与凭空捏造的响应——转换器只输出有依据的内容。工程化与回归保障0.7.0 起转换测试不再依赖云端下载而是把 Postman 转换夹具直接纳入仓库PR #8901fixtures/input/ 下存放了 26 个覆盖典型场景的集合包括AuthBasic、AuthBearer、AuthMultiple、Folders、FormData、FormUrlencoded、GetMethods、Headers、MultipleServers、NestedServers、OperationIds、ParseStatusCode、PathParams、RawBody、Responses、SimplePost、UrlWithPort、XLogo等测试对每个夹具执行convert并与snapshots/convert.test.ts.snap 中的快照比对convert.test.ts。夹具输出以 2 空格缩进写入保证数据未变化时重新生成不会产生噪音 diff。此外还有覆盖各内部模块的单元测试如 auth.test.ts、urls 相关测试 等以及 convert.test.ts 中针对合并、tag 策略、索引筛选、错误输入等行为的用例。输出阶段还有一道pruneDocument兜底递归删除所有undefined值并清掉空tags、空security、空components、空externalDocs保证文档可直接序列化、可被任何 OpenAPI 工具链消费prune-document.ts。版本演进一览从 CHANGELOG 可以清晰看到这个包从hello world到成熟转换器的演进轨迹版本里程碑关键变化0.1.0诞生hello world :)依赖scalar/oas-utils起步0.1.8结构修复Postman 示例不再被误判为文件夹0.2.0脚本与净化导入 post-response 脚本去掉默认 tag 与捏造响应/raw、/路径专门处理0.3.0环境基线要求 Node 200.4.0重大重构major refactor, improves everything, keeps all the data now0.5.0环境升级要求 Node 22LTS随scalar/openapi-types0.6.0 同步升级0.6.0重复操作mergeOperation支持同路径同方法的多请求0.6.3合并深化请求变体示例/响应/脚本保留结构等价路径参数统一集合识别共享状态码感知的响应描述0.7.0转换质量keepHeaders保留头策略服务器 URL 变量解析与 server variables 生成leaf tag 默认策略 chain 回退无响应体状态码省略 content夹具入库0.7.12 / 0.7.17 / 0.7.19发布治理README 生成器元数据修复scalarReadmenpm trusted publishing 重发无功能变化其中 0.7.12 的修复颇具工程启示npm 会把package.json中的readme字段当作 README 文本本身导致部分包被发布成字面量[object Object]因此重命名为scalarReadme并重发PR #9710。结语在 Scalar 生态中的位置scalar/postman-to-openapi是 Scalar 导入链路的地基无论 API Client 中导入 Postman 集合还是文档平台中把既有 Postman 工作流迁移到 OpenAPI最终都落在这套转换语义上。0.7.x 系列之后它已经具备变量解析、变体合并、路径统一、脚本保留、标签策略、增量合并等完整的工业级能力而仓库内的 26 个夹具与快照测试为后续演进提供了坚实的回归保障。需要进一步深入时可以从 convert.ts 的ConvertOptions入手逐层阅读 helpers 下的实现与测试即可完整掌握每一次转换决策背后的原理。【免费下载链接】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),仅供参考