跳至主要內容
Skip to content

API 文件與 OpenAPI:Swagger 實戰

好的 API 需要好的文件。OpenAPI(原 Swagger)是 API 文件的業界標準。本篇將介紹如何使用它。


一、 什麼是 OpenAPI?

1.1 概念

OpenAPI Specification(OAS)是一種 API 描述格式:

  • 標準化的 API 文件格式
  • 可被機器讀取
  • 可自動生成互動式文件
  • 可生成客戶端 SDK(Software Development Kit,軟體開發套件)

1.2 生態系統


二、 OpenAPI 規範

2.1 基本結構

yaml
openapi: 3.0.0
info:
  title: My API
  version: 1.0.0
  description: A sample API

servers:
  - url: https://api.example.com/v1
    description: Production
  - url: http://localhost:3000/v1
    description: Development

paths:
  /users:
    # endpoints...

components:
  schemas:
    # data models...
  securitySchemes:
    # auth methods...

2.2 定義 Path

yaml
paths:
  /users:
    get:
      summary: List all users
      description: Returns a list of users
      tags:
        - Users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        200:
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"
                  pagination:
                    $ref: "#/components/schemas/Pagination"
        401:
          $ref: "#/components/responses/Unauthorized"

    post:
      summary: Create a user
      tags:
        - Users
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUser"
      responses:
        201:
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

  /users/{id}:
    get:
      summary: Get a user
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        404:
          $ref: "#/components/responses/NotFound"

2.3 定義 Schema

yaml
components:
  schemas:
    User:
      type: object
      required:
        - id
        - name
        - email
      properties:
        id:
          type: string
          example: "123"
        name:
          type: string
          example: "John Doe"
        email:
          type: string
          format: email
          example: "john@example.com"
        createdAt:
          type: string
          format: date-time
          example: "2025-01-13T00:00:00Z"

    CreateUser:
      type: object
      required:
        - name
        - email
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8

    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer

    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string

2.4 定義認證

yaml
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

    apiKey:
      type: apiKey
      in: header
      name: X-API-Key

security:
  - bearerAuth: []

# 針對特定端點覆蓋
paths:
  /public:
    get:
      security: [] # 不需要認證

三、 Express 整合

3.1 swagger-jsdoc

從程式碼註解生成文件:

javascript
const swaggerJsdoc = require("swagger-jsdoc");
const swaggerUi = require("swagger-ui-express");

const options = {
  definition: {
    openapi: "3.0.0",
    info: {
      title: "My API",
      version: "1.0.0",
    },
    servers: [{ url: "http://localhost:3000/api" }],
  },
  apis: ["./routes/*.js"],
};

const spec = swaggerJsdoc(options);
app.use("/docs", swaggerUi.serve, swaggerUi.setup(spec));

3.2 JSDoc 註解

javascript
/**
 * @openapi
 * /users:
 *   get:
 *     summary: List users
 *     tags: [Users]
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *     responses:
 *       200:
 *         description: Success
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/User'
 */
router.get("/", async (req, res) => {
  // ...
});

/**
 * @openapi
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       properties:
 *         id:
 *           type: string
 *         name:
 *           type: string
 */

3.3 使用 YAML 檔案

javascript
const YAML = require("yamljs");
const swaggerDocument = YAML.load("./openapi.yaml");

app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));

四、 互動式文件

4.1 Swagger UI

javascript
const swaggerUi = require("swagger-ui-express");

app.use(
  "/docs",
  swaggerUi.serve,
  swaggerUi.setup(spec, {
    customCss: ".swagger-ui .topbar { display: none }",
    customSiteTitle: "My API Docs",
    swaggerOptions: {
      persistAuthorization: true,
    },
  })
);

4.2 ReDoc

javascript
const redoc = require("redoc-express");

app.get(
  "/docs",
  redoc({
    title: "My API",
    specUrl: "/openapi.json",
  })
);

app.get("/openapi.json", (req, res) => {
  res.json(spec);
});

五、 程式碼生成

5.1 生成客戶端

bash
# 安裝 OpenAPI Generator
npm install @openapitools/openapi-generator-cli -g

# 生成 TypeScript 客戶端
openapi-generator-cli generate \
  -i openapi.yaml \
  -g typescript-fetch \
  -o ./client

5.2 使用生成的客戶端

typescript
import { UsersApi, Configuration } from "./client";

const config = new Configuration({
  basePath: "https://api.example.com",
  accessToken: "your-token",
});

const usersApi = new UsersApi(config);

// 完全類型安全
const users = await usersApi.getUsers({ page: 1, limit: 20 });

六、 契約測試

6.1 驗證回應符合規範

javascript
const OpenAPIValidator = require("express-openapi-validator");

app.use(
  OpenAPIValidator.middleware({
    apiSpec: "./openapi.yaml",
    validateRequests: true,
    validateResponses: true,
  })
);

// 錯誤處理
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({
    error: {
      code: "VALIDATION_ERROR",
      message: err.message,
      errors: err.errors,
    },
  });
});

6.2 測試時驗證

javascript
const { OpenAPISchemaValidator } = require("openapi-schema-validator");

describe("API", () => {
  it("should match OpenAPI spec", async () => {
    const response = await request(app).get("/users");

    const validator = new OpenAPISchemaValidator({ version: 3 });
    const result = validator.validate(spec);

    expect(result.errors).toHaveLength(0);
  });
});

七、 最佳實踐

7.1 組織結構

/docs
  /openapi
    openapi.yaml          # 主文件
    /paths
      users.yaml
      products.yaml
    /schemas
      user.yaml
      product.yaml
    /responses
      errors.yaml

7.2 使用 $ref 分割

yaml
# openapi.yaml
paths:
  /users:
    $ref: "./paths/users.yaml"

components:
  schemas:
    User:
      $ref: "./schemas/user.yaml"

7.3 版本控制

yaml
# v1/openapi.yaml
info:
  version: 1.0.0

# v2/openapi.yaml
info:
  version: 2.0.0

總結

工具用途
OpenAPI SpecAPI 描述語言
Swagger UI互動式文件
swagger-jsdoc從註解生成
OpenAPI Generator生成客戶端
express-openapi-validator契約驗證

> **文件即程式碼**——將 OpenAPI 納入版本控制,讓文件與程式碼同步更新。


進階挑戰

  1. 為你的 API 完整撰寫 OpenAPI 規範。
  2. 設置 CI/CD(Continuous Integration/Continuous Deployment,持續整合/持續部署)在合併前驗證 API 符合規範。
  3. 研究 AsyncAPI,了解如何描述事件驅動的 API。

延伸閱讀與資源