🛑 1. The Problem First: API ที่ "คุยกันคนละภาษา"
ลองมองการออกแบบ API แบบจูเนียร์ที่เน้นเร็วและสะดวกตัวเอง:
# ❌ Naive Approach: ใช้ HTTP Method มั่ว และ Status Code 200 อย่างเดียว
POST /get-user-data-now?id=123
# ผลลัพธ์: { "status": "error", "message": "Not found" } และ HTTP 200 OK!
ปัญหา: เมื่อคุณใช้ HTTP 200 สำหรับทุกอย่าง (แม้แต่ตอน Error) ระบบ Caching ของ Browser/CDN จะงง และ Frontend ต้องเขียน if-else เช็คข้างในวุ่นวายมหาศาล และการตั้งชื่อแบบ "Action" (get-user-data) จะทำให้ API คุณไม่เป็นระบบเมื่อโปรเจกต์โตขึ้น คุยกันคนละภาษาคือจุดเริ่มต้นของความหายนะในทีมครับ
💡 2. Real-Life Analogy: ร้านอาหารที่มีเมนู (REST) vs บุฟเฟต์ตามสั่ง (GraphQL)
- REST API: เหมือน "ร้านอาหารตามสั่ง (A La Carte)". คุณสั่งเมนูเบอร์ 1 (GET /users) คุณได้ข้าวมันไก่แบบเซ็ตมาตรฐานมาเลย คุณเลือกไม่ได้ว่าจะเอาแค่ข้าวข้อนึง ไก่ชิ้นนึง (Over-fetching)
- GraphQL: เหมือน "บุฟเฟต์ที่เชฟทำตามสั่งเป๊ะๆ". คุณยื่นกระดาษบอกว่า "ขอข้าว 2 ช้อน ไก่ 3 ชิ้น ไม่เอาแตงกวา" (Query) แล้วเชฟจะจัดใส่จานมาให้ตามนั้นเป๊ะ ไม่ขาดไม่เกิน
- Status Codes: เหมือน "สัญญาณไฟหน้าร้าน".
- เขียว (200): เลือกเมนูได้
- แดง (401): ต้องโชว์บัตรสมาชิกก่อนเข้า
- ส้ม (400): สั่งเมนูที่ไม่มีในร้าน
🚀 3. Execution Journey: มหากาพย์ N+1 และทางแก้ระดับโปร
ปัญหาที่พบบ่อยที่สุดใน API คือ N+1 Problem (ยิง Query เยอะเกินจำเป็น)
🛠 Step-by-step:
- The Trap: ดึงรายชื่อ User 10 คน (1 Query)
- The Loop: สำหรับ User แต่ละคน ให้ไปดึง Posts ของเขาออกมา (10 Queries)
- The Result: กลายเป็นยิง Database 11 ครั้ง! (ช้าและเปลือง)
- The Fix (DataLoader): รวบ ID ทั้งหมด (1, 2, 3...) แล้วยิง Query เดียวหา Posts ทั้งหมดที่เกี่ยวข้อง (
WHERE id IN (1,2,3...))
// ✅ Best Practice: ใช้ DataLoader เพื่อ Batch ข้อมูล
const batchPosts = async (authorIds) => {
const posts = await db.post.findMany({
where: { authorId: { in: authorIds } }, // 🛠 ยิงครั้งเดียวจบ!
});
return authorIds.map((id) => posts.filter((p) => p.authorId === id));
};
🪤 4. The Junior Trap: โรค "หวงข้อมูล/ส่งข้อมูลมั่ว"
จูเนียร์มักจะส่ง Error Message จาก Database ให้ Frontend โดยตรง:
// ❌ Junior Trap: รั่วไหลข้อมูลความปลอดภัย
res.status(500).json({
error:
"Internal Server Error: Table 'users' does not have column 'password_plain'",
});
ระวัง: การส่ง Error ลึกๆ แบบนี้คือการ "บอกโครงสร้าง Database ให้ Hacker". ✅ การแก้ไข: กรอง Error เสมอ! ส่งแค่รหัส Error หรือข้อความทั่วไปที่ User เข้าใจได้ ส่วน Error จริงๆ ให้ Log เก็บไว้ดูหลังบ้านคนเดียว
⚖️ 5. The Why Matrix: REST vs GraphQL
| หัวข้อ | REST (มาตรฐาน) | GraphQL (ยืดหยุ่น) |
|---|---|---|
| Caching | ⚡⚡⚡ ง่ายมาก (ใช้ Browser/CDN ได้เลย) | ยาก (ต้องทำที่ Client) |
| ปริมาณข้อมูล | มักจะได้เกิน (Over-fetching) | ⚡⚡⚡ แม่นยำ (ขอเท่าไหร่ได้เท่านั้น) |
| การเรียนรู้ | ง่าย ใครๆ ก็เป็น | ปานกลาง (ต้องเรียน Query Language) |
| Versioning | ต้องทำ /v1/, /v2/ | ใช้วิธี Deprecated (ไม่ต้องเปลี่ยน URL) |
🎓 6. Senior Mindset Summary
การเป็น Senior คือการมองว่า "API คือสัญญา (Contract) ระหว่างทีม". ก่อนจะเขียนโค้ดบรรทัดแรก คุณต้องตกลง "ภาษา" และ "Status Code" กับ Frontend ให้จบก่อน สิ่งนี้เรียกว่า Contract-First Design ซึ่งจะช่วยลดงานแก้ในอนาคตได้หลักร้อยชั่วโมงครับ!