Files
nest-template/CLAUDE.md
2026-04-14 11:49:30 +08:00

257 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NestJS Template — 開發規範
## 技術棧
| 分類 | 套件 |
|---|---|
| Runtime | Node.jsESM`"type": "module"` |
| 框架 | NestJS 11 |
| 語言 | TypeScript 5`NodeNext` 模組解析) |
| 套件管理 | pnpm |
| 資料庫 | PostgreSQL`@nestjs/typeorm` + `typeorm` + `pg` |
| 快取/佇列 | Redis`ioredis` |
| 設定管理 | `@nestjs/config``.env` 載入) |
| 驗證 | `class-validator` + `class-transformer` |
| 日誌 | `nestjs-pino` + `pino-roll` |
| API 文件 | `@nestjs/swagger`(僅非 production路徑 `/api/docs` |
| 資安 | `helmet` + NestJS 內建 CORS |
| 流量控制 | `@nestjs/throttler` |
| 認證 | `@nestjs/jwt` |
## 目錄結構
```
src/
├── core/ # NestJS 生命週期類別 + 基礎設施模組
│ ├── db/
│ │ └── db.module.ts
│ ├── redis/
│ │ └── redis.module.ts
│ ├── logger/
│ │ └── logger.module.ts
│ ├── throttler/
│ │ └── throttler.module.ts
│ ├── filter/
│ │ └── http-exception.filter.ts
│ ├── interceptor/
│ │ └── transform.interceptor.ts
│ ├── pipe/
│ │ └── validation-exception.factory.ts
│ ├── guard/
│ │ └── jwt.guard.ts
│ └── <類型>/ # middleware …
├── common/ # 共用但不掛生命週期的內容
│ ├── token/
│ │ └── tokens.ts # 注入 token 常數REDIS_CLIENT 等)
│ ├── type/
│ │ ├── response.ts # ApiResponse<T> 統一回傳型別
│ │ └── jwt-payload.ts # JWT payload 型別
│ ├── decorator/
│ │ └── public.decorator.ts # @Public() 跳過 JWT 驗證
│ └── <類型>/ # util、constant …
├── module/ # 功能模組(每個領域一個子目錄)
│ └── <模組名>/
│ ├── dtos/
│ ├── entities/
│ ├── <模組名>.controller.ts
│ ├── <模組名>.service.ts
│ └── <模組名>.module.ts
├── app.module.ts
└── main.ts
```
### `core/` — 生命週期類別與基礎設施模組
**任何檔案都必須放在對應的子目錄中,不可直接置於 `core/` 根層。**
| 子目錄 | 內容 |
|---|---|
| `filter/` | 例外處理,`@Catch()` |
| `interceptor/` | 請求/回應攔截,`NestInterceptor` |
| `guard/` | 路由守衛,`CanActivate` |
| `pipe/` | 輸入轉換與驗證相關,`PipeTransform` 及 factory |
| `middleware/` | Express 中介層,`NestMiddleware` |
| `db/` | TypeORM / PostgreSQL 設定 |
| `redis/` | Redis 設定 |
| `logger/` | Pino logger 設定 |
| `throttler/` | Rate limiting 設定 |
### `common/` — 共用工具
不掛生命週期、跨模組共用的內容。**任何檔案都必須放在對應的子目錄中,不可直接置於 `common/` 根層。**
| 子目錄 | 內容 |
|---|---|
| `token/` | 注入 token 常數(`REDIS_CLIENT` 等) |
| `type/` | 共用 TypeScript 型別interfaceenum |
| `decorator/` | 自訂裝飾器 |
| `util/` | 純函式工具 |
| `constant/` | 一般常數 |
### `module/` — 功能模組
每個業務領域一個子目錄,結構依複雜度選擇:
簡單模組(單一 controller / service
```
module/<模組名>/
├── dtos/
├── entities/
├── <模組名>.controller.ts
├── <模組名>.service.ts
└── <模組名>.module.ts
```
複雜模組(多個 controller / service
```
module/<模組名>/
├── dtos/
├── entities/
├── controllers/
│ ├── <子功能>.controller.ts
│ └── ...
├── services/
│ ├── <子功能>.service.ts
│ └── ...
└── <模組名>.module.ts
```
> 單一檔案與目錄切分可混用service 需要拆分但 controller 不需要時controller 維持單檔即可。
## 全域基礎設施
以下透過 `APP_*` token 在 `AppModule` 全域註冊,所有模組自動套用。執行順序如下:
1. `ThrottlerGuard` — rate limiting
2. `JwtAuthGuard` — JWT 驗證
3. `ValidationPipe` — 請求驗證
4. `TransformInterceptor` — 回應包裝
5. `HttpExceptionFilter` — 例外捕捉
### 統一回傳格式(`ApiResponse<T>`
所有端點一律回傳 `{ code, message, data }` 結構:
```typescript
// 成功
{ "code": 200, "message": "ok", "data": { ... } }
// 失敗
{ "code": 404, "message": "...", "data": null }
```
- `TransformInterceptor``core/interceptor/`)— 包裝成功回應
- `HttpExceptionFilter``core/filter/`)— 捕捉所有例外並統一格式
### ValidationPipe
| 選項 | 值 | 說明 |
|---|---|---|
| `whitelist` | `true` | 自動剔除 DTO 未宣告的屬性 |
| `forbidNonWhitelisted` | `true` | 傳入未宣告屬性時回傳 400 |
| `transform` | `true` | 自動將 payload 轉為 DTO 實例 |
| `exceptionFactory` | `validationExceptionFactory` | forbidden 欄位顯示 `不允許的欄位:<field>` |
### JWT 認證
- 全域啟用 `JwtAuthGuard`,預設所有路由需要有效 Bearer token
- 公開路由加上 `@Public()` decorator 跳過驗證
- Payload 型別定義於 `common/type/jwt-payload.ts``sub` 為 user ID
- 需要 Swagger 測試時在端點加上 `@ApiBearerAuth()`
```typescript
@Public() // 跳過 JWT
@SkipThrottle() // 跳過 rate limiting@nestjs/throttler 提供)
```
### Rate Limiting
透過 `THROTTLE_TTL`(毫秒)與 `THROTTLE_LIMIT`(次數)設定,預設 60 秒內最多 60 次請求。
## 日誌策略
使用 `nestjs-pino`,以 `NODE_ENV` 切換行為:
| 環境 | 輸出 |
|---|---|
| 非 production | `pino-pretty`colorize輸出至 stdout |
| production | `pino-roll` 寫入檔案 |
**production 檔案規則:**
| 類型 | 路徑 | 檔名格式 | 保留天數 |
|---|---|---|---|
| app log | `logs/app/` | `app.yyyy-MM-dd.log` | 14 天 |
| error log | `logs/error/` | `error.yyyy-MM-dd.log` | 30 天 |
**每筆 request 自動記錄:** `method``url``query``params``ip`(含 X-Forwarded-For`userAgent``statusCode``responseTime`
## 資安
### Helmet
全域套用 `helmet()` Express middleware設定標準安全 HTTP headersCSP、HSTS、X-Frame-Options 等)。
### CORS
透過 `CORS_ORIGINS` 環境變數控制允許的來源,多個來源以逗號分隔。預設空陣列(全封)。
| 設定 | 值 |
|---|---|
| `methods` | GET、POST、PUT、PATCH、DELETE |
| `credentials` | `true` |
## Swagger
- 僅在非 production 環境啟用,路徑為 `/api/docs`
- 預設已加入 `addBearerAuth()`,需要保護的端點加上 `@ApiBearerAuth()`
- DTO 屬性使用 `@ApiProperty()` 補充文件
## 環境變數
參考 `.env.example`
```
NODE_ENV=development
PORT=3000
CORS_ORIGINS=http://localhost:5173,http://localhost:3000
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=nest_db
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_USER= # 選填
REDIS_PASSWORD= # 選填
JWT_SECRET=change-me
JWT_EXPIRES_IN=7d
THROTTLE_TTL=60000 # 毫秒
THROTTLE_LIMIT=60 # 次數
```
## 編碼規範
- 所有相對路徑 import 必須加 `.js` 副檔名NodeNext 規定)
- 禁止 `any`,型別須明確宣告
- 無註解 — 程式碼透過命名與結構自我表達
- 正式程式碼不留 `console.log`(使用注入的 `Logger`
- 類別屬性在適用情況下使用 `readonly`
## 常用指令
```bash
pnpm start:dev # 開發模式watch
pnpm build # 編譯
pnpm start:prod # 執行編譯後的產物
pnpm test # 單元測試
pnpm test:cov # 測試覆蓋率
```