Modeling Resources
ก่อนจะตั้งชื่อ URI สักตัว ให้ตัดสินใจก่อนว่า อะไร คือ resource ของคุณ การสร้างแบบจำลอง resource ที่ดีจะทำให้ส่วนที่เหลือของ API ลงตัวไปเอง ส่วนการสร้างแบบจำลองที่ไม่ดีจะโผล่ขึ้นมาในภายหลังในรูปของ endpoint ที่ดูเก้กังและกรณียกเว้นต่าง ๆ
ค้นหาคำนาม
หัวข้อที่มีชื่อว่า “ค้นหาคำนาม”เริ่มจากภาษาของโดเมน สิ่งที่คงอยู่ถาวรซึ่ง user ของคุณพูดถึง เช่น orders, invoices, playlists, devices คือ resource หลักของคุณ โดยทั่วไปจะ map ไปยัง collection ที่คุณสร้าง อ่าน อัปเดต และลบ
// The resources of a small blog API, as TypeScript shapes.type User = { id: string; name: string; email: string };type Article = { id: string; title: string; body: string; authorId: string };type Comment = { id: string; body: string; articleId: string; authorId: string };ไม่ใช่ทุกตารางใน database จะเป็น resource และไม่ใช่ทุก resource จะเป็นตาราง resource คือสิ่งใดก็ตามที่มีค่าพอจะอ้างถึงด้วย URI รวมถึงสิ่งที่คำนวณได้หรือเป็นสิ่งเสมือน (virtual) เช่น /me (user ปัจจุบัน) หรือ /search
Resource ไม่ใช่ action
หัวข้อที่มีชื่อว่า “Resource ไม่ใช่ action”เมื่อ use case ฟังดูเหมือนคำกริยา เช่น “publish an article”, “cancel an order” ให้ต้านทานความอยากที่จะคิดค้น /publishArticle ขึ้นมา มีทางเลือกที่สะอาดกว่าอยู่สองทาง:
- จำลองการเปลี่ยนแปลงให้เป็นการอัปเดต field:
PATCH /articles/42ด้วย{ "status": "published" } - จำลอง action ให้เป็น sub-resource ที่ action นั้นสร้างขึ้น:
POST /orders/42/cancellation
Granularity
หัวข้อที่มีชื่อว่า “Granularity”ตั้งเป้าให้ resource ไม่หยาบเกินไป (ก้อน /data ยักษ์ก้อนเดียว) และไม่ละเอียดเกินไป (แยก resource หนึ่งตัวต่อหนึ่ง field) วิธีทดสอบที่ดี: resource ควรเป็นสิ่งที่ client ต้องการดึง สร้าง หรือเปลี่ยนแปลงในฐานะหน่วยเดียว
| ข้อดี (Resource Modeling) | ข้อแลกเปลี่ยน |
|---|---|
| API predictable — developer รู้ URI โดยเดาได้ | ต้องวิเคราะห์ domain ก่อน — ไม่รีบ implement ได้ทันที |
| resource-based ทำให้ API evolve ง่ายกว่า verb-based | action บางอย่าง map เป็น resource ได้ยาก เช่น search, publish |
| HTTP method บอก intent — ไม่ต้องอ่าน body | business logic ซับซ้อนอาจต้องการ sub-resource พิเศษ |
| cacheable ได้ตาม HTTP standard | over-normalization ทำให้ต้อง call หลาย endpoint |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”Verb-based Endpoint อาการ:
POST /getUser,POST /createOrder,POST /cancelOrder- ทุก operation ใช้ POST — cache ไม่ได้, idempotency ไม่ชัด
- แก้:
GET /users/\{id\},POST /orders,POST /orders/\{id\}/cancellation
Resource Granularity ผิด อาการ:
- resource เดียวเก็บทุกอย่าง:
GET /everything?type=user&id=1 - หรือแตก resource ละเอียดเกิน:
GET /users/\{id\}/name,GET /users/\{id\}/email - resource ควรเป็นสิ่งที่ client ต้องการดึงหรือแก้ไขในฐานะหน่วยเดียว
💡 ตัวอย่างจากของจริง
GitHub API:
/repos/\{owner\}/\{repo\}— repository เป็น resource/repos/\{owner\}/\{repo\}/issues— issues เป็น sub-collection/repos/\{owner\}/\{repo\}/issues/\{number\}— issue หนึ่งชิ้นStripe API:
/customers/\{id\}— customer resource/customers/\{id\}/payment_methods— payment method เป็น sub-resource ของ customer