@umijs/openapi, swagger-jsdoc, and swagger-ui-express are npm packages that help developers work with OpenAPI (formerly Swagger) specifications in JavaScript environments, but they serve distinct roles in the documentation lifecycle. @umijs/openapi is a code generation tool that consumes an OpenAPI spec to produce TypeScript clients or hooks for frontend applications. swagger-jsdoc generates OpenAPI specification files from JSDoc-style comments in source code, primarily used in Node.js backends. swagger-ui-express serves a visual, interactive OpenAPI documentation UI via an Express middleware, typically mounted in a server application to expose API docs at a route like /api-docs. Together, they address different stages: spec authoring (swagger-jsdoc), client code generation (@umijs/openapi), and human-readable documentation serving (swagger-ui-express).
These three packages all relate to OpenAPI documentation, but they solve very different problems in the development workflow. Confusing them leads to architectural mismatches — like trying to serve a UI from a frontend bundle or generate client code from JSDoc alone. Let’s clarify what each does, how they’re used, and where they fit.
@umijs/openapi takes an existing OpenAPI spec and generates TypeScript code for frontend consumption.
// Example: openapi.config.ts
import { defineConfig } from '@umijs/openapi';
export default defineConfig({
schemaPath: 'http://localhost:8080/v3/api-docs', // or local ./openapi.json
serversPath: './src/services',
});
// After running `umi openapi`, you get:
// src/services/user.ts
export async function getUser(id: number): Promise<User> {
return request(`/users/${id}`, { method: 'GET' });
}
swagger-jsdoc takes JSDoc comments in your source code and builds an OpenAPI spec.
// Example: routes/users.js
/**
* @swagger
* /users/{id}:
* get:
* summary: Get user by ID
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: A user object
*/
app.get('/users/:id', (req, res) => {
// ...
});
// Build script
const swaggerJsdoc = require('swagger-jsdoc');
const options = { definition: { /*...*/ }, apis: ['./routes/*.js'] };
const openapiSpec = swaggerJsdoc(options);
// Writes openapi.json
swagger-ui-express takes an OpenAPI spec and serves an interactive HTML UI via Express.
// Example: server.js
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./openapi.json');
const app = express();
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
// Now visit http://localhost:3000/api-docs
💡 Key insight: These tools are often used together, not as alternatives. For example:
swagger-jsdoc→ generates spec →swagger-ui-expressserves it →@umijs/openapiconsumes it to generate frontend code.
If your team writes APIs first and documents them inline:
swagger-jsdoc to extract specs from JSDoc.swagger-ui-express to expose docs.openapi.json with frontend teams.@umijs/openapi to generate typed services.If you start with a hand-written OpenAPI spec (e.g., in openapi.yaml):
swagger-ui-express to serve the static spec.@umijs/openapi directly against the YAML file.swagger-jsdoc entirely.@umijs/openapi: Requires a valid OpenAPI 3.0+ document (URL or local file). Fails if spec is invalid.swagger-jsdoc: Requires JSDoc blocks tagged with @swagger and a config object defining API paths. Outputs a JSON spec.swagger-ui-express: Requires a JavaScript object or JSON representing the spec. No validation — garbage in, broken UI out.@umijs/openapi: Produces TypeScript files with fetch wrappers or React hooks.swagger-jsdoc: Produces a single OpenAPI JSON object.swagger-ui-express: Produces HTML, CSS, and JS assets served dynamically by Express.@umijs/openapi runs at build time (or manually via CLI). It’s a code generator, not a runtime library. The output is plain .ts files committed to source control.
// package.json script
"scripts": {
"gen:api": "umi openapi"
}
swagger-jsdoc also runs at build time or during server startup. Often integrated into CI or dev scripts to keep openapi.json up to date.
// During server init
const specs = swaggerJsdoc(options);
fs.writeFileSync('openapi.json', JSON.stringify(specs));
swagger-ui-express runs at runtime inside an Express server. It’s middleware that responds to HTTP requests with the Swagger UI.
// Only active when server is running
app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec));
swagger-ui-express in a Create React App or Vite project. It needs Node.js and Express — it won’t work in the browser.@umijs/openapi to parse JSDoc. It only reads OpenAPI specs, not source code comments.swagger-ui-express. It needs a fully formed spec object, which swagger-jsdoc can provide.Here’s how they work together in a full-stack project:
Step 1: Backend uses swagger-jsdoc
// src/routes/user.js
/**
* @swagger
* /api/v1/users:
* get:
* summary: List all users
* responses:
* 200:
* description: User list
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/User'
*/
router.get('/users', controller.list);
Step 2: Generate and serve spec
// src/server.js
const swaggerSpec = swaggerJsdoc({
definition: { openapi: '3.0.0', info: { title: 'API' } },
apis: ['./src/routes/*.js']
});
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// Also write to disk for frontend
fs.writeFileSync('./public/openapi.json', JSON.stringify(swaggerSpec));
Step 3: Frontend generates clients with @umijs/openapi
// openapi.config.ts
export default defineConfig({
schemaPath: './public/openapi.json',
serversPath: './src/api',
mockFolder: './mocks',
});
// Run: npx umi openapi
// Output: ./src/api/user.ts with typed functions
Now frontend devs import getUser() with full TypeScript support, while backend docs stay live at /api-docs.
| Package | Best Used When… | Not Suitable For… |
|---|---|---|
@umijs/openapi | You have a stable OpenAPI spec and need auto-generated TypeScript API clients. | Generating specs from code or serving UI docs. |
swagger-jsdoc | You write API routes in JS/TS and want docs co-located with implementation. | Pure frontend projects or static spec workflows. |
swagger-ui-express | You run an Express server and need to expose interactive API documentation. | Client-side apps or non-Express backends. |
These tools aren’t competitors — they’re complementary pieces of an API documentation pipeline. Choose based on your role:
@umijs/openapi to consume specs.swagger-jsdoc to author specs and swagger-ui-express to publish them.Avoid forcing one tool to do another’s job. Keep the concerns separate: spec authoring, spec serving, and client generation. That’s how you get reliable, maintainable API integrations.
Choose @umijs/openapi when you already have a valid OpenAPI specification (YAML or JSON) and need to automatically generate strongly typed TypeScript service clients or React hooks for consumption in a frontend application. It’s ideal for frontend teams working against well-defined backend contracts, especially in Umi.js-based projects, though it works independently. Avoid if your OpenAPI spec is unstable or frequently changing without version control, as regenerated code may break existing integrations.
Choose swagger-jsdoc when your API implementation lives in a JavaScript/TypeScript codebase and you prefer writing API documentation inline using JSDoc comments that get compiled into a machine-readable OpenAPI spec. This approach keeps documentation close to implementation logic and suits teams that maintain both backend and spec in the same repository. Be aware that complex OpenAPI features may not map cleanly to JSDoc syntax, requiring fallbacks to raw YAML fragments.
Choose swagger-ui-express when you need to serve an interactive, browser-based OpenAPI documentation interface from an Express.js server. It’s the go-to solution for exposing human-readable API docs alongside your backend, often paired with a generated or hand-written spec file. Do not use it in pure frontend-only projects—it requires a running Express server and is not intended for client-side rendering or static site generation.
A powerful tool that generates TypeScript request code from OpenAPI 3.0 documentation. If you're using umi, you might want to check out @umijs/plugin-openapi plugin instead.
npm i --save-dev @umijs/openapi
# or
pnpm add -D @umijs/openapi
# or
yarn add -D @umijs/openapi
openapi2ts.config.ts or .openapi2tsrc.ts:export default {
schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
serversPath: './servers',
}
For multiple API sources, you can use an array configuration:
export default [
{
schemaPath: 'http://app.swagger.io/v2/swagger.json',
serversPath: './servers/app',
},
{
schemaPath: 'http://auth.swagger.io/v2/swagger.json',
serversPath: './servers/auth',
}
]
package.json:{
"scripts": {
"openapi2ts": "openapi2ts"
}
}
npm run openapi2ts
| Property | Required | Description | Type | Default |
|---|---|---|---|---|
| requestLibPath | No | Custom request method path | string | - |
| requestOptionsType | No | Custom request options type | string | {[key: string]: any} |
| requestImportStatement | No | Custom request import statement | string | - |
| apiPrefix | No | API prefix | string | - |
| serversPath | No | Output directory path | string | - |
| schemaPath | No | Swagger 2.0 or OpenAPI 3.0 URL | string | - |
| projectName | No | Project name | string | - |
| authorization | No | Documentation authentication token | string | - |
| namespace | No | Namespace name | string | API |
| mockFolder | No | Mock directory | string | - |
| enumStyle | No | Enum style | string-literal | enum | string-literal |
| nullable | No | Use null instead of optional | boolean | false |
| dataFields | No | Data fields in response | string[] | - |
| isCamelCase | No | Use camelCase for files and functions | boolean | true |
| declareType | No | Interface declaration type | type/interface | type |
| splitDeclare | No | Generate a separate .d.ts file for each tag group. | boolean | - |
| Property | Type | Description |
|---|---|---|
| afterOpenApiDataInited | (openAPIData: OpenAPIObject) => OpenAPIObject | Hook after OpenAPI data initialization |
| customFunctionName | (data: APIDataType) => string | Custom request function name |
| customTypeName | (data: APIDataType) => string | Custom type name |
| customClassName | (tagName: string) => string | Custom class name |
| customType | (schemaObject, namespace, originGetType) => string | Custom type getter |
| customFileNames | (operationObject, apiPath, _apiMethod) => string[] | Custom file name generator |
一个强大的工具,可以根据 OpenAPI 3.0 文档生成 TypeScript 请求代码。如果你使用 umi,可以考虑使用 @umijs/plugin-openapi 插件。
npm i --save-dev @umijs/openapi
# 或者
pnpm add -D @umijs/openapi
# 或者
yarn add -D @umijs/openapi
openapi2ts.config.ts 或 .openapi2tsrc.ts:export default {
schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
serversPath: './servers',
}
如果需要处理多个 API 源,可以使用数组配置:
export default [
{
schemaPath: 'http://app.swagger.io/v2/swagger.json',
serversPath: './servers/app',
},
{
schemaPath: 'http://auth.swagger.io/v2/swagger.json',
serversPath: './servers/auth',
}
]
package.json 中添加生成脚本:{
"scripts": {
"openapi2ts": "openapi2ts"
}
}
npm run openapi2ts
| 属性 | 必填 | 说明 | 类型 | 默认值 |
|---|---|---|---|---|
| requestLibPath | 否 | 自定义请求方法路径 | string | - |
| requestOptionsType | 否 | 自定义请求方法 options 参数类型 | string | {[key: string]: any} |
| requestImportStatement | 否 | 自定义请求方法表达式 | string | - |
| apiPrefix | 否 | API 前缀 | string | - |
| serversPath | 否 | 生成文件夹的路径 | string | - |
| schemaPath | 否 | Swagger 2.0 或 OpenAPI 3.0 的地址 | string | - |
| projectName | 否 | 项目名称 | string | - |
| authorization | 否 | 文档登录凭证 | string | - |
| namespace | 否 | 命名空间名称 | string | API |
| mockFolder | 否 | mock 目录 | string | - |
| enumStyle | 否 | 枚举样式 | string-literal | enum | string-literal |
| nullable | 否 | 使用 null 代替可选 | boolean | false |
| dataFields | 否 | response 中数据字段 | string[] | - |
| isCamelCase | 否 | 小驼峰命名文件和请求函数 | boolean | true |
| declareType | 否 | interface 声明类型 | type/interface | type |
| splitDeclare | 否 | 每个tag组一个独立的.d.ts. | boolean | - |
| 属性 | 类型 | 说明 |
|---|---|---|
| afterOpenApiDataInited | (openAPIData: OpenAPIObject) => OpenAPIObject | OpenAPI 数据初始化后的钩子 |
| customFunctionName | (data: APIDataType) => string | 自定义请求方法函数名称 |
| customTypeName | (data: APIDataType) => string | 自定义类型名称 |
| customClassName | (tagName: string) => string | 自定义类名 |
| customType | (schemaObject, namespace, originGetType) => string | 自定义获取类型 |
| customFileNames | (operationObject, apiPath, _apiMethod) => string[] | 自定义生成文件名 |