ข้ามไปยังเนื้อหา

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

เมื่อ use case ฟังดูเหมือนคำกริยา เช่น “publish an article”, “cancel an order” ให้ต้านทานความอยากที่จะคิดค้น /publishArticle ขึ้นมา มีทางเลือกที่สะอาดกว่าอยู่สองทาง:

  • จำลองการเปลี่ยนแปลงให้เป็นการอัปเดต field: PATCH /articles/42 ด้วย { "status": "published" }
  • จำลอง action ให้เป็น sub-resource ที่ action นั้นสร้างขึ้น: POST /orders/42/cancellation

ตั้งเป้าให้ resource ไม่หยาบเกินไป (ก้อน /data ยักษ์ก้อนเดียว) และไม่ละเอียดเกินไป (แยก resource หนึ่งตัวต่อหนึ่ง field) วิธีทดสอบที่ดี: resource ควรเป็นสิ่งที่ client ต้องการดึง สร้าง หรือเปลี่ยนแปลงในฐานะหน่วยเดียว

ข้อดี (Resource Modeling)ข้อแลกเปลี่ยน
API predictable — developer รู้ URI โดยเดาได้ต้องวิเคราะห์ domain ก่อน — ไม่รีบ implement ได้ทันที
resource-based ทำให้ API evolve ง่ายกว่า verb-basedaction บางอย่าง map เป็น resource ได้ยาก เช่น search, publish
HTTP method บอก intent — ไม่ต้องอ่าน bodybusiness logic ซับซ้อนอาจต้องการ sub-resource พิเศษ
cacheable ได้ตาม HTTP standardover-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
โดยปกติแล้วควรจำลอง "publish an article" อย่างไร?
ข้อความใดเกี่ยวกับ resource และตารางใน database ที่ถูกต้อง?
อะไรคือวิธีทดสอบ granularity ของ resource ที่ดี?