Backend Architecture (NestJS)
apps/api คือ NestJS 11 REST API monolith ที่ลงทะเบียน 20 feature modules ไว้ใน AppModule — ทุก module เป็น self-contained (controller + service + DTOs) และ communicate กันผ่าน DI
High-Level Architecture
HTTP Request
│
▼
┌────────────────────┐
│ Express adapter │ ← from @nestjs/platform-express
└────────┬───────────┘
▼
┌────────────────────┐
│ CORS middleware │ main.ts — auto-allow *.pages.dev + explicit list
└────────┬───────────┘
▼
┌────────────────────┐
│ Global Guards │ JwtAuthGuard (default), ApiKeyGuard (integration only)
└────────┬───────────┘
▼
┌────────────────────┐
│ RolesGuard │ @RequireModule / @Roles → check user.adminModules*
└────────┬───────────┘
▼
┌────────────────────┐
│ Interceptors │ IdempotencyInterceptor (integration only)
└────────┬───────────┘
▼
┌────────────────────┐
│ ValidationPipe │ class-validator → 400 if DTO fails
└────────┬───────────┘
▼
┌────────────────────┐
│ Controller │ thin — delegates to service
└────────┬───────────┘
▼
┌────────────────────┐
│ Service │ business logic + PrismaService calls
└────────┬───────────┘
▼
┌────────────────────┐
│ PrismaClient │ PostgreSQL (pooled via PgBouncer)
└────────────────────┘Module Registry
จาก apps/api/src/app.module.ts — ลงทะเบียน 20 modules:
| # | Module | Path | บทบาท |
|---|---|---|---|
| 1 | PrismaModule | infrastructure/prisma | Global PrismaService — DI ทุก module |
| 2 | HealthModule | modules/health | /health/live, /health/ready, /health/stats |
| 3 | AuthModule | modules/auth | login/register/me + JWT strategy + guards |
| 4 | MasterDataModule | modules/master-data | items, locations, partners, uoms, warehouses (5 controllers) |
| 5 | InboundModule | modules/inbound | asn, grn, putaway, putaway-rules |
| 6 | InventoryModule | modules/inventory | stock, movements, adjustments, cycle-counts |
| 7 | OutboundModule | modules/outbound | orders, waves, pick-tasks, shipments |
| 8 | ReturnsModule | modules/returns | rma |
| 9 | ConfigWmsModule | modules/config | system config keys (8-tab settings) |
| 10 | WebhookModule | modules/webhook | webhook subscriptions + deliveries |
| 11 | AuditModule | modules/audit | audit log query |
| 12 | FilesModule | modules/files | R2 presigned uploads |
| 13 | ApprovalsModule | modules/approvals | threshold-based approval queue |
| 14 | ReplenishmentModule | modules/replenishment | min/max rules + tasks |
| 15 | UsersModule | modules/users | user CRUD + permissions |
| 16 | AnnouncementsModule | modules/announcements | banner messages |
| 17 | ApiKeysModule | modules/api-keys | API key CRUD + stats + IP/origin/rate config |
| 18 | ReportsModule | modules/reports | 22 aggregated GET endpoints |
| 19 | IntegrationModule | modules/integration | /integration/v1/* push API |
| 20 | LpnModule | modules/lpn | License Plate CRUD + movements |
ดูรายละเอียดทุก module: Module Reference
Module Folder Convention
modules/<feature>/
├─ <feature>.module.ts # @Module — imports + providers + controllers
├─ <feature>.controller.ts # routes + decorators
├─ <feature>.service.ts # business logic
└─ dto/
├─ create-<feature>.dto.ts
└─ update-<feature>.dto.tsตัวอย่างจริง (modules/auth/):
auth/
├─ auth.module.ts
├─ auth.controller.ts
├─ auth.service.ts
├─ auth.service.spec.ts ← unit test (Jest)
├─ dto/
│ ├─ login.dto.ts
│ └─ register.dto.ts
├─ guards/
│ ├─ jwt-auth.guard.ts
│ ├─ api-key.guard.ts
│ ├─ roles.guard.ts
│ ├─ api-key-rate-limit.ts
│ └─ ip-cidr.util.ts
├─ decorators/
│ ├─ public.decorator.ts ← @Public() bypass JWT
│ ├─ current-user.decorator.ts ← @CurrentUser() param
│ ├─ roles.decorator.ts ← @Roles('admin', …)
│ ├─ require-module.decorator.ts← @RequireModule('items', 'write')
│ └─ api-key.decorator.ts ← @ApiKey() + @ApiKeyScopes(…)
├─ interfaces/
│ └─ jwt-payload.interface.ts
└─ strategies/
└─ jwt.strategy.tsโมดูลที่มี sub-controllers (master-data, inbound, inventory, outbound, returns) จะ nest อีกชั้นเช่น inbound/asn/, inbound/grn/, inbound/putaway/
Global Pipes
จาก main.ts:
typescript
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // strip props ไม่อยู่ใน DTO
forbidNonWhitelisted: true, // ตอบ 400 ถ้ามี extra props
transform: true, // string → number / Date / class instance
}));ผลคือทุก DTO ที่ใช้ class-validator decorators (@IsString, @IsUUID, @IsEmail, …) จะถูก validate อัตโนมัติ
Guards
JwtAuthGuard
- ใช้กับทุก route ยกเว้น ที่ติด
@Public()decorator - อ่าน Bearer token จาก
Authorizationheader → verify ด้วยJWT_SECRET - Attach payload ไว้ที่
request.user
ApiKeyGuard
- ใช้กับ
/integration/v1/*เท่านั้น (controller-level@UseGuards) - อ่าน
X-API-KeyหรือAuthorization: ApiKey … - SHA-256 hash → lookup ที่
api_keytable - เช็คตามลำดับ: validity → IP whitelist (exact + CIDR) → allowed origins (CORS) → rate limit (sliding 60s)
- Attach ApiKey record ที่
request.apiKey
RolesGuard
- ทำงานหลัง JwtAuthGuard
- Admin / superadmin → bypass ทุกอย่าง
- เช็ค
@Roles('manager', 'supervisor')หรือ@RequireModule('items', 'write')กับuser.adminModules/user.adminModulesWrite
ดูเต็ม: Authorization
Interceptors
IdempotencyInterceptor (modules/integration/idempotency.interceptor.ts)
- รัน เฉพาะ integration controller
- ถ้า request มี
Idempotency-Keyheader:- คำนวณ SHA-256 ของ body
- lookup
integration_requestด้วย (tenantId, idempotencyKey, endpoint) - ถ้าเจอ + body hash ตรง → replay cached response (header
Idempotent-Replay: true) - ถ้าเจอ + body hash ไม่ตรง →
409 IDEMPOTENCY_KEY_MISMATCH - ถ้าไม่เจอ → run handler + บันทึก response
- TTL: 24 ชั่วโมง
Error Handling
- NestJS built-in
HttpExceptionครอบ standard codes (400BadRequestException, 401UnauthorizedException, 403ForbiddenException, 404NotFoundException, 409ConflictException) - ValidationPipe throw
BadRequestExceptionพร้อมmessages: string[] - Custom error codes (เช่น
IDEMPOTENCY_KEY_MISMATCH) ใช้ throwHttpException({ code, message }, status)ตรง ๆ - ยังไม่มี global ExceptionFilter — error format ใช้ค่า default ของ NestJS
Logging
- ใช้
Loggerของ Nest (new Logger(ClassName.name)) - Print ไป stdout — Render.com capture log อัตโนมัติ
- Bootstrap log:
WMS API [<NODE_ENV>] listening on port <PORT> - Auth, ApiKeyGuard log สำคัญทุกอัน (login success/failed, key validation, rate limit hit)
Where to find…
| สิ่งที่ต้องการ | path |
|---|---|
| HTTP route definitions | src/modules/*/*.controller.ts |
| Business logic | src/modules/*/*.service.ts |
| Validation rules | src/modules/*/dto/*.dto.ts (class-validator decorators) |
| Database queries | service files (PrismaService) |
| Database schema | prisma/schema.prisma |
| Auth logic | src/modules/auth/auth.service.ts |
| Permission check | src/modules/auth/guards/roles.guard.ts |
| Env validation | src/app.module.ts (Joi schema) |
| Bootstrap config | src/main.ts |