OpenAPI / Swagger 文件生成

API 文件不應該手寫——手寫的文件一定會和實際行為脫節。各框架的自動生成方式:

FastAPI:零設定,內建

# 什麼都不需要做,/docs 和 /redoc 自動可用
app = FastAPI(
    title="My API",
    description="User management API",
    version="1.0.0",
)
 
@app.post("/users", response_model=UserResponse, status_code=201,
    summary="Create a new user",
    responses={400: {"model": ErrorResponse}},
)
async def create_user(user: UserCreate):
    ...

FastAPI 的 OpenAPI 是從 Pydantic schema 推斷的,不需要額外標注。

NestJS:@nestjs/swagger

import { ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger';
 
@Post()
@ApiOperation({ summary: 'Create a new user' })
@ApiBody({ type: CreateUserDto })
@ApiResponse({ status: 201, type: UserDto })
@ApiResponse({ status: 400, description: 'Validation error' })
create(@Body() dto: CreateUserDto) {
  return this.usersService.create(dto);
}
 
// main.ts — 設定 Swagger
const config = new DocumentBuilder()
  .setTitle('My API')
  .setVersion('1.0')
  .addBearerAuth()
  .build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);

NestJS 需要在 handler 上加 @Api* annotation,比 FastAPI 多幾行,但比手寫 YAML 好太多。

Spring Boot:springdoc-openapi

// build.gradle
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.x'
 
// application.yml
springdoc:
  api-docs:
    path: /api-docs
  swagger-ui:
    path: /swagger-ui.html
 
// Controller 加 annotation(可選,不加也有基本文件)
@Operation(summary = "Create a new user")
@ApiResponse(responseCode = "201", description = "User created")
@PostMapping
public UserDto create(@RequestBody @Valid UserCreateDto dto) { ... }

Spring Boot 只需要加一個依賴就有 /swagger-ui.html,非常省事。

Express:需要自己選

Express 沒有自動生成,主流選項:

  • swagger-jsdoc:在 route / controller 加 JSDoc annotation,從 JSDoc 生成 OpenAPI YAML
  • tsoa:TypeScript decorator + class 定義,compile time 生成 routes 和 swagger
  • Hono hono/zod-openapi:zod schema + Hono 的整合,自動生成 OpenAPI

DI Container Debug

當 NestJS / Spring 的 DI 出問題(circular dependency、Bean not found),錯誤訊息通常不直觀。

NestJS:找 circular dependency

# NestJS 的錯誤訊息會說 "Nest can't resolve dependencies of..."
# 加 forwardRef 解 circular reference
@Injectable()
export class UserService {
  constructor(
    @Inject(forwardRef(() => EmailService))
    private emailService: EmailService,
  ) {}
}

NestJS 的 @nestjs/core 提供了 NestFactory.createApplicationContext() 讓你在不啟 HTTP server 的情況下 debug DI 設定:

const app = await NestFactory.createApplicationContext(AppModule);
const userService = app.get(UserService);
// 確認 UserService 能被正確解析

Spring:Actuator 查 Bean

Spring Boot Actuator 的 /actuator/beans endpoint 列出所有 Bean 和它們的依賴關係,對 debug DI 非常有用:

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: beans, health, info

框架專屬 Lint 規則

NestJS:eslint-plugin-nestjs

檢查:

  • Module 沒有正確 export 但被其他 module import
  • @Injectable() 少標
  • 在 constructor 裡做 async 操作(應該放在 onModuleInit

Spring:SonarQube / Spring 專屬規則

SonarQube 有 Spring-specific 規則:

  • @Transactional 加在 private method 上(Spring proxy 不會攔截,transaction 不生效)
  • @Autowired 用在 field 而非 constructor(不建議,難 test)
  • Bean 在 test context 沒有正確 mock

Python FastAPI:mypy + pylance + pydantic 驗證

FastAPI + Pydantic 最重要的工具是 mypy 靜態型別檢查:

mypy app/ --strict

Pylance(VS Code)的 Pydantic plugin 讓 IDE 知道 Pydantic model 的欄位型別,有 auto-complete 和錯誤提示。


開發輔助工具清單

框架CLIHot ReloadDB GUIAPI Testing
Express無(ts-node-dev / nodemon)ts-node-devTablePlus / pgAdminPostman / Bruno
FastAPI無(uvicorn —reload)uvicorn —reloadTablePlus / pgAdmin/docs 內建
NestJS@nestjs/clinest g controllerts-node-devTablePlusSwagger UI
Spring BootSpring InitializrSpring DevToolsIntelliJ DB ToolSwagger UI
LaravelArtisan CLI(php artisan make:*無(FPM 每次都是新 process)TablePlusSwagger(需額外設定)

延伸閱讀