@umijs/openapi vs swagger-jsdoc vs swagger-ui-express
Generating and Serving OpenAPI Documentation in JavaScript Projects
@umijs/openapiswagger-jsdocswagger-ui-express

Generating and Serving OpenAPI Documentation in JavaScript Projects

@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).

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@umijs/openapi02123 kB07 months agoMIT
swagger-jsdoc01,787712 kB443 years agoMIT
swagger-ui-express01,49524 kB532 years agoMIT

Generating and Serving OpenAPI Docs: @umijs/openapi vs swagger-jsdoc vs 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.

🧩 Core Purpose: What Problem Does Each Solve?

@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-express serves it → @umijs/openapi consumes it to generate frontend code.

🔄 Workflow Integration: Where Do They Fit?

Backend-First Spec Authoring

If your team writes APIs first and documents them inline:

  1. Use swagger-jsdoc to extract specs from JSDoc.
  2. Feed that spec into swagger-ui-express to expose docs.
  3. Share the generated openapi.json with frontend teams.
  4. Frontend uses @umijs/openapi to generate typed services.

Contract-First Development

If you start with a hand-written OpenAPI spec (e.g., in openapi.yaml):

  1. Backend implements routes to match the contract.
  2. Use swagger-ui-express to serve the static spec.
  3. Frontend runs @umijs/openapi directly against the YAML file.
  4. Skip swagger-jsdoc entirely.

⚙️ Configuration and Output Differences

Input Requirements

  • @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.

Output Type

  • @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.

🌐 Runtime vs Build-Time

@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));

🛑 Common Misuses to Avoid

  • Don’t use swagger-ui-express in a Create React App or Vite project. It needs Node.js and Express — it won’t work in the browser.
  • Don’t expect @umijs/openapi to parse JSDoc. It only reads OpenAPI specs, not source code comments.
  • Don’t feed raw JSDoc to swagger-ui-express. It needs a fully formed spec object, which swagger-jsdoc can provide.

🔁 Real Integration Example

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.

📊 Summary: When to Use Which

PackageBest Used When…Not Suitable For…
@umijs/openapiYou have a stable OpenAPI spec and need auto-generated TypeScript API clients.Generating specs from code or serving UI docs.
swagger-jsdocYou write API routes in JS/TS and want docs co-located with implementation.Pure frontend projects or static spec workflows.
swagger-ui-expressYou run an Express server and need to expose interactive API documentation.Client-side apps or non-Express backends.

💡 Final Recommendation

These tools aren’t competitors — they’re complementary pieces of an API documentation pipeline. Choose based on your role:

  • Frontend engineers: Reach for @umijs/openapi to consume specs.
  • Backend engineers: Use swagger-jsdoc to author specs and swagger-ui-express to publish them.
  • Full-stack teams: Combine all three for a seamless contract-driven workflow.

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.

How to Choose: @umijs/openapi vs swagger-jsdoc vs swagger-ui-express

  • @umijs/openapi:

    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.

  • swagger-jsdoc:

    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.

  • swagger-ui-express:

    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.

README for @umijs/openapi

OpenAPI to TypeScript Generator (OpenAPI TypeScript 生成器)

GitHub Repo stars npm (scoped) GitHub tag (latest SemVer pre-release)

English | 中文

English

Introduction

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.

Installation

npm i --save-dev @umijs/openapi
# or
pnpm add -D @umijs/openapi
# or
yarn add -D @umijs/openapi

Usage

  1. Create a configuration file in your project root directory. You can name it either 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',
  }
]
  1. Add the generation script to your package.json:
{
  "scripts": {
    "openapi2ts": "openapi2ts"
  }
}
  1. Generate the API code:
npm run openapi2ts

Configuration Options

PropertyRequiredDescriptionTypeDefault
requestLibPathNoCustom request method pathstring-
requestOptionsTypeNoCustom request options typestring{[key: string]: any}
requestImportStatementNoCustom request import statementstring-
apiPrefixNoAPI prefixstring-
serversPathNoOutput directory pathstring-
schemaPathNoSwagger 2.0 or OpenAPI 3.0 URLstring-
projectNameNoProject namestring-
authorizationNoDocumentation authentication tokenstring-
namespaceNoNamespace namestringAPI
mockFolderNoMock directorystring-
enumStyleNoEnum stylestring-literal | enumstring-literal
nullableNoUse null instead of optionalbooleanfalse
dataFieldsNoData fields in responsestring[]-
isCamelCaseNoUse camelCase for files and functionsbooleantrue
declareTypeNoInterface declaration typetype/interfacetype
splitDeclareNoGenerate a separate .d.ts file for each tag group.boolean-

Custom Hooks

PropertyTypeDescription
afterOpenApiDataInited(openAPIData: OpenAPIObject) => OpenAPIObjectHook after OpenAPI data initialization
customFunctionName(data: APIDataType) => stringCustom request function name
customTypeName(data: APIDataType) => stringCustom type name
customClassName(tagName: string) => stringCustom class name
customType(schemaObject, namespace, originGetType) => stringCustom 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

使用方法

  1. 在项目根目录创建配置文件,可以命名为 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',
  }
]
  1. package.json 中添加生成脚本:
{
  "scripts": {
    "openapi2ts": "openapi2ts"
  }
}
  1. 生成 API 代码:
npm run openapi2ts

配置选项

属性必填说明类型默认值
requestLibPath自定义请求方法路径string-
requestOptionsType自定义请求方法 options 参数类型string{[key: string]: any}
requestImportStatement自定义请求方法表达式string-
apiPrefixAPI 前缀string-
serversPath生成文件夹的路径string-
schemaPathSwagger 2.0 或 OpenAPI 3.0 的地址string-
projectName项目名称string-
authorization文档登录凭证string-
namespace命名空间名称stringAPI
mockFoldermock 目录string-
enumStyle枚举样式string-literal | enumstring-literal
nullable使用 null 代替可选booleanfalse
dataFieldsresponse 中数据字段string[]-
isCamelCase小驼峰命名文件和请求函数booleantrue
declareTypeinterface 声明类型type/interfacetype
splitDeclare每个tag组一个独立的.d.ts.boolean-

自定义钩子

属性类型说明
afterOpenApiDataInited(openAPIData: OpenAPIObject) => OpenAPIObjectOpenAPI 数据初始化后的钩子
customFunctionName(data: APIDataType) => string自定义请求方法函数名称
customTypeName(data: APIDataType) => string自定义类型名称
customClassName(tagName: string) => string自定义类名
customType(schemaObject, namespace, originGetType) => string自定义获取类型
customFileNames(operationObject, apiPath, _apiMethod) => string[]自定义生成文件名