🛑 1. The Problem First: "คำตอบที่ไม่เคยซ้ำกัน" (Inconsistent API Response)
ลองนึกภาพว่าคุณเป็น Frontend ที่ต้องเชื่อมต่อกับ API 10 ตัว แต่ละตัวส่งคำตอบกลับมาคนละท่า:
// API 1: ส่งแค่ data
{ "id": 1, "name": "Joe" }
// API 2: ส่งห่อ data ไว้ข้างใน
{ "result": { "id": 2, "name": "Jane" } }
// API 3: เวลาพังส่ง Error เป็น String ธรรมดา
"User not found"
ปัญหา: การที่ API ไม่มีมาตรฐานทำให้ Frontend ต้องเขียนโค้ด if-else เช็คโครงสร้างทุุกครั้งที่ยิง API (Boilerplate มหาศาล) และเมื่อระบบมีขนาดใหญ่ขึ้น การไล่หาว่า Error เกิดขึ้นที่ไฟล์ไหนเวลาพังใน Production จะกลายเป็นเรื่องที่เป็นไปไม่ได้เลย นี่คือเหตุผลที่เราต้องมี "ตัวกลาง" ในการจัดระเบียบครับ
💡 2. Real-Life Analogy: พนักงานแพ็คของและประกันภัย (Packer & Insurance)
- Interceptor: เหมือน "พนักงานแพ็คของท้ายสายพาน". ไม่ว่าเชฟจะทำอาหารอะไรมา (Data) พนักงานคนนี้จะเอาใส่กล่องแบรนด์บริษัทที่มีหน้าตาสวยงามและแปะป้ายวันผลิต (Timestamp) ก่อนส่งถึงลูกค้าเสมอ
- Exception Filter: เหมือน "แผนกเคลมสินค้า". เมื่อเกิดอุบัติเหตุในร้าน (Code Crash) พนักงานคนนี้จะเข้ามาคุมสถานการณ์ ไม่ปล่อยให้ลูกค้าเห็นความวุ่นวายหลังบ้าน แต่จะยื่น "ใบเคลม" (Custom Error JSON) ที่ระบุชัดเจนว่าเกิดอะไรขึ้นและต้องทำยังไงต่อ
- Logger: เหมือน "กล่องดำของเครื่องบิน". มันบันทึกทุุกการเคลื่อนไหวของระบบ เพื่อให้เวลาเครื่องตก (System Crash) เราสามารถเอาข้อมูลออกมาวิเคราะห์ได้ว่า "ใครคือจำเลย"
🚀 3. Execution Journey: ขั้นตอนการสร้างระเบียบโลกใหม่
เราจะใช้พลังของ NestJS ในการดักจับข้อมูลทุุกหยดที่ไหลเข้า-ออกแอป
🛠 Step-by-step:
- Standardize Response: ใช้
Interceptorห่อข้อมูลทุุกอันด้วย Schema เดียวกัน - Catch Exceptions: ใช้
Exception Filterเพื่อแปลง Error น่ากลัวๆ ให้เป็น JSON ที่อ่านออก - Audit Log: ใช้
Loggerที่มี Context ชัดเจน (รู้ว่า Error มาจากไฟล์ไหน บรรทัดไหน)
// ✅ Best Practice: แปลงร่างทุุก Response ให้มีมาตรฐาน
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
return next.handle().pipe(
map((data) => ({
data, // 🛠 ห่อหุ้มข้อมูลจริง
statusCode: context.switchToHttp().getResponse().statusCode,
timestamp: new Date().toISOString(),
})),
);
}
}
🪤 4. The Junior Trap: โรค "Try-Catch ทุกที่"
จูเนียร์มักจะกลัวโค้ดพังจนต้องเขียน try-catch หุ้มทุุกฟังก์ชัน:
// ❌ Junior Trap: เขียนโค้ดซ้ำซ้อนจนอ่านไม่ออก
async findAll() {
try {
return await this.userRepo.find();
} catch (e) {
throw new HttpException("พังจ้า", 500); // 🌋 เขียนแบบนี้ 100 ที่ก็พ่ายแพ้ 100 รอบ
}
}
ระวัง: การ try-catch ทุุกจุดคือการสร้าง Technical Debt มหาศาล
✅ การแก้ไข: ปล่อยให้ Error มันพุ่งขึ้นไป (Bubble up) แล้วให้ Global Exception Filter ตัวที่เดียวจัดการ ท่านี้จะทำให้โค้ด Business Logic ของคุณสะอาดขึ้น 80% ทันที!
⚖️ 5. The Why Matrix: ทำไมต้องแยก Interceptor กับ Filter?
| หัวข้อ | Interceptor (ขาสำเร็จ) | Exception Filter (ขาพัง) |
|---|---|---|
| หน้าที่หลัก | ตกแต่ง/เปลี่ยนแปลงข้อมูล | ดักจับและจัดระเบียบ Error |
| จังหวะการทำงาน | ก่อนและหลังเข้า Method | เมื่อเกิด Exception เท่านั้น |
| ความยืดหยุ่น | ⚡⚡⚡ สูง (เปลี่ยนค่าได้เลย) | ⚡⚡ ชัดเจน (โฟกัสแค่เรื่อง Error) |
| เหมาะกับ | Formatting, Logging, Caching | API Error, Validation, Logging |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "ความล้มเหลวคือสิ่งที่คาดเดาได้". การออกแบบ API ที่ดีไม่ใช่การทำให้มันไม่ล่ม แต่คือการทำให้เมื่อมันล่มแล้ว "ระบบยังสื่อสารได้อย่างเป็นมืออาชีพ" และ "ทิ้งร่องรอยให้ตามแก้ได้ง่าย" ครับ!