- ในบทความต้นฉบับของ Roy Fielding ว่าด้วย REST ไม่ได้กำหนดอย่างชัดเจนว่าต้องใช้ HTTP methods หรือ API ที่ยึด CRUD เป็นศูนย์กลาง และเดิมที REST เน้นการออกแบบระบบแบบ hypermedia-driven (HATEOAS)
- สิ่งที่หลายคนเรียกว่า RESTful API มักไม่ได้นำไปใช้จริงตาม ข้อจำกัดสำคัญของ REST (โดยเฉพาะการใช้ hypermedia)
- Resource ไม่ได้จำกัดอยู่แค่โครงสร้างข้อมูลหรือเอนทิตี แต่ครอบคลุมทุกแนวคิดที่สามารถระบุได้ด้วย URI
- ตาม กฎ 6 ข้อของ Fielding แก่นสำคัญคือการนำทางที่ขับเคลื่อนด้วย hypermedia, ความเป็นอิสระจากโปรโตคอล, และการให้ความสำคัญกับ media type
- ในความเป็นจริง API ส่วนใหญ่มักถูกออกแบบให้ใกล้เคียงสไตล์ RPC มากกว่าจะเป็น RESTful อย่างแท้จริง เพราะ ความสะดวกของเครื่องมือเอกสารอย่าง OpenAPI เป็นต้น
ทำไม RESTful API ส่วนใหญ่จึงไม่ใช่ RESTful อย่างแท้จริง
- บทความหลักของ Roy Fielding (2000) นำเสนอ REST (Representational State Transfer) เป็นรูปแบบอุดมคติของการออกแบบซอฟต์แวร์บนเครือข่าย และอธิบายว่าเป็นหลักการที่อยู่เบื้องหลังความสำเร็จของเว็บ
- ในบทความไม่ได้กำหนดให้ การใช้ HTTP methods หรือการออกแบบแบบ CRUD เป็นข้อบังคับ และเน้นว่าเมื่อมุ่งสู่ REST แก่นสำคัญคือการเปลี่ยนสถานะบนพื้นฐานของ hypermedia (HATEOAS), อินเทอร์เฟซแบบสากล และการโต้ตอบที่ยึด resource เป็นศูนย์กลาง
ความเข้าใจผิดเรื่อง Hypermedia (HATEOAS) และ REST
- Fielding ชี้ชัดว่า API ที่ไม่มี hypermedia ไม่ถือว่าเป็น RESTful
- "ถ้าเอนจินไม่ได้ถูกขับเคลื่อนด้วย hypertext ก็ไม่ใช่ RESTful"
- HATEOAS คือวิธีที่ไคลเอนต์ สำรวจการกระทำแบบไดนามิกโดยตามลิงก์ ที่ฝังอยู่ในคำตอบจากเซิร์ฟเวอร์
- การใช้เพียงอินเทอร์เฟซแบบ HTTP/CRUD ไม่ใช่แก่นแท้ของ REST
ความเข้าใจผิดที่พบบ่อยเกี่ยวกับ REST
- มองว่า REST คือ CRUD (ทั้งที่จริงเป็นแนวคิดที่กว้างกว่านั้น)
- เข้าใจผิดว่า resource คือ entity (โครงสร้างข้อมูลบนเซิร์ฟเวอร์)
- อ้างว่า RESTful API ห้ามใช้คำกริยา (Verb)
- สิ่งเหล่านี้เป็นเพียงการตัดสินใจด้านการออกแบบ ไม่ได้เกี่ยวข้องโดยตรงกับแก่นของ REST
ความหมายที่แท้จริงของการขับเคลื่อนด้วย Hypermedia (HATEOAS)
- จุดประสงค์ของ HATEOAS คือ ลดระดับการผูกติดกันระหว่างไคลเอนต์กับเซิร์ฟเวอร์ให้น้อยที่สุด
- เมื่อโครงสร้าง URI ของเซิร์ฟเวอร์เปลี่ยน จะช่วยลดภาระจากการต้องนำไคลเอนต์ไป deploy ใหม่ ทำให้ ขยายระบบและพัฒนาต่อได้ง่ายขึ้น
- ไคลเอนต์จะสำรวจพฤติกรรมการทำงานผ่าน hyperlink ภายใน response โดยไม่ต้องพึ่งเอกสารหรือความรู้ล่วงหน้า
{
"orderId": 123,
"_links": {
"self": { "href": "/orders/123" },
"cancel": { "href": "/orders/123/cancel", "method": "POST" }
}
}
- ดังตัวอย่างนี้ การจะเข้าใกล้ RESTful อย่างแท้จริง คำตอบควร รวมการกระทำ (ลิงก์) ไว้ใน response
Resource ใน REST คืออะไร?
- Resource คือทุกสิ่งที่สามารถระบุได้ด้วย URI
- รวมถึงเอกสาร รูปภาพ วัตถุทางกายภาพ บริการ และแนวคิดเชิงนามธรรม
- บนเซิร์ฟเวอร์ไม่ได้มี “resource” อยู่จริงเป็นชิ้น ๆ แต่มีเพียงโครงสร้างการแมปเชิงนามธรรมที่เข้าถึงได้ผ่าน URI
- ใน RFC 3986 ก็ ไม่ได้จำกัดขอบเขตของ resource
- อาจรวมถึงเอกสารอิเล็กทรอนิกส์ รูปภาพ แหล่งข้อมูล บริการ หรือแม้แต่คน นิติบุคคล และหนังสือ
กฎ 6 ข้อของ RESTful API ตามที่ Fielding นิยามไว้
- 1. ต้องไม่ผูกติดกับโปรโตคอลเดียว
- การระบุตัวตนด้วย URI ควรถูกใช้งานได้กับทุกโปรโตคอล
- 2. ต้องไม่ดัดแปลงมาตรฐานของโปรโตคอล (เช่น HTTP) ตามอำเภอใจ
- สามารถชดเชยข้อบกพร่องของมาตรฐานได้ แต่ห้ามเพิ่มหรือเปลี่ยนกฎเองโดยพลการ
- 3. ควรโฟกัสที่การนิยาม media type (format) แทนโครงสร้าง URI
- เน้นรูปแบบข้อมูลและการนิยามลิงก์ ไม่เสียพลังงานไปกับการระบุเส้นทางหรือเขียนเอกสารของ path
- 4. ห้าม hardcode การตั้งชื่อ URI หรือโครงสร้างลำดับชั้นแบบตายตัว
- เพื่อไม่ให้ไคลเอนต์ต้องเดาหรือยึดติดกับโครงสร้าง namespace ของเซิร์ฟเวอร์ ควรชี้นำด้วยการนำทางแบบลิงก์
- 5. ห้ามเปิดเผยประเภทของ resource (การจัดหมวดหมู่ภายใน)
- ประเภทวัตถุภายในไม่มีความหมายต่อไคลเอนต์ ควรเปิดเผยเฉพาะ media type มาตรฐานและลิงก์
- 6. ควรต้องรู้เพียง bookmark (URI เริ่มต้น) ที่เหลือให้สำรวจผ่านลิงก์ใน response
- ไคลเอนต์ควรรู้เพียง media type มาตรฐาน และการเปลี่ยนสถานะต้องเกิดขึ้นบนพื้นฐานของ hyperlink ในคำตอบจากเซิร์ฟเวอร์เสมอ
การตีความกฎและการประยุกต์ใช้จริง
- การจะเข้าใกล้ RESTful อย่างแท้จริง ต้อง ลดการผูกติดกับโปรโตคอล URI และประเภทต่าง ๆ ให้น้อยที่สุด
- API ควรเน้นที่รูปแบบของข้อมูลและลิงก์ (media type) และไคลเอนต์ไม่จำเป็นต้องรู้ล่วงหน้าเกี่ยวกับโครงสร้าง URI หรือกฎการตั้งชื่อ
- เช่น แทนที่จะระบุ path อย่าง
/users/123/activate ไว้ตรง ๆ ควรชี้นำการทำงานผ่านลิงก์ "activate" ใน response
เหตุผลที่ RESTful API จริง ๆ พบได้ไม่บ่อย
- ความสะดวกของเครื่องมืออย่าง OpenAPI และ Swagger มักถูกให้ความสำคัญในงานพัฒนา
- เพราะมีประโยชน์จริง เช่น การสร้างเอกสารอัตโนมัติ การสร้างโค้ด และการตรวจสอบความถูกต้อง
- ในสภาพแวดล้อมแบบ SPA ที่ไคลเอนต์และเซิร์ฟเวอร์พัฒนาโดยทีมเดียวกัน ความจำเป็นของการแก้ปัญหาการผูกติดกับ URI ไม่ได้สูงมาก ทำให้ข้อดีของ HATEOAS ไม่เด่นชัด
- ภาระในการเรียนรู้หลักการ REST ตั้งแต่ต้น และความซับซ้อนของการ parse ลิงก์แบบไดนามิก เป็นอุปสรรคในเชิงปฏิบัติที่สำคัญ
บทสรุป
- ตามกฎของ Fielding RESTful API ที่แท้จริงจำเป็นต้องมีการนำทางแบบไดนามิกบนพื้นฐานของ hypermedia (HATEOAS)
- REST ไม่ใช่เพียงการทำ CRUD บน HTTP แต่เป็นสถาปัตยกรรมที่เน้น การผูกติดกันแบบหลวม ความสามารถในการพัฒนาเปลี่ยนแปลงต่อได้ และการเปลี่ยนสถานะแบบไดนามิก เช่นเดียวกับหลักการของเว็บ
- ในโลกจริง ความเหมาะสมเชิงปฏิบัติและบริบทของทีมอาจสำคัญกว่า
- หากเป็น public API สำหรับนักพัฒนาภายนอก การนำ HATEOAS มาใช้เป็นสิ่งที่แนะนำ แต่ถ้าเป็น API ใช้ภายใน สไตล์ RPC ที่เรียบง่ายก็อาจใช้งานได้จริงกว่า
- แก่นสำคัญของการออกแบบ API คือทำให้ เรียนรู้ได้ง่ายและใช้งานผิดได้ยาก ไม่จำเป็นต้องเป็น RESTful เสมอไป
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ผมเข้าใจความสุดโต่งทางหลักการที่โผล่มาในที่นี้ และก็อ่านวิทยานิพนธ์ของ Fielding อย่างสนใจเหมือนกัน แต่รู้สึกว่านี่เป็นสงครามที่จบไปแล้ว พอได้ยินคำว่า REST API ก็แทบจะเดาได้เลยว่า
ฟังก์ชันแบบนี้ ซึ่งมันจะเรียก
ฝั่งแบ็กเอนด์ก็มีฟังก์ชันประมาณว่า
และใช้อะโนเทชันเพื่ออธิบายการแมป URL สุดท้ายมันก็คือ RPC นั่นแหละ แค่ยังคงอยู่ในรูปแบบที่ทำมือกว่า ซับซ้อนน้อยกว่า และนักพัฒนาควบคุมได้มากกว่าระบบที่ทั้งซับซ้อนและไม่เป็นธรรมชาติ ปัญหา 80% ของงานส่วนใหญ่เกิดจากคนไม่ใช้รูปแบบวันที่ ISO 8601
ผมไม่ค่อยเข้าใจว่าทำไมถึงมีคนมองว่านี่เป็น "การต่อสู้" ที่ต้องใส่ใจ แนวคิด REST มีประโยชน์ แต่ส่วน HATEOAS แทบไม่มีประโยชน์ในทางปฏิบัติและมีแต่สร้างปัญหา ถ้าดู Richardson maturity model จุดสูงสุดของ REST คือการมีองค์ประกอบอย่าง HATEOAS ด้วย ผมก็ไม่เข้าใจตรรกะที่ว่าถ้าไม่มี HATEOAS ก็ไม่ใช่ REST เพราะมันไม่ได้แตกต่างอย่างมีนัยสำคัญนัก ถ้า HATEOAS แทบไม่มีความหมายในทางปฏิบัติ การยึดติดกับการจัดประเภทแบบเคร่งหลักการนี้ก็ดูไม่มีประโยชน์ Richardson maturity model
ในส่วนที่บอกว่า "ทีมเถียงเรื่อง status code มากเกินไปและมักใช้ไม่ตรง HTTP spec" ขอเน้นว่า 401 Unauthorized ควรใช้เมื่อยังไม่ได้ยืนยันตัวตน และ 403 Forbidden ควรใช้เมื่อไม่มีสิทธิ์
ผมมักจะถามคนอื่นว่าตั้งใจจะใช้ความหมายของ REST ตามที่นิยามไว้ใน paper จริงไหม ปกติผมเป็นฝั่งที่ไม่ยอมรับการใช้คำไร้ความหมายแบบพร่ำเพรื่อ สุดท้ายก็มักจะลงเอยด้วย "อ๋อ ก็หมายถึง web API ธรรมดา" แล้วก็ข้ามไป ความต่างสำคัญคือ API แบบนี้แต่ละตัวจะมีจุดเพี้ยนของตัวเองที่เราต้องทำความเข้าใจ
สำหรับผม นัยสำคัญที่สำคัญจริง ๆ คือ "hypermedia link" อาจดูเหมือนเป็นสิ่ง "ทั่วไป" ผ่าน link type หลากหลายแบบ (อยู่ใน HTTP header หรือผลลัพธ์ที่ส่งกลับ) แต่ในทางปฏิบัติ REST ยุคนี้ก็ทำงานไม่ต่างกัน ตัวอย่างเช่น ถ้าออกแบบพลาดแล้วจริง ๆ ควรเป็น "enable" ไม่ใช่ "activate" สุดท้ายก็ต้องเปลี่ยนจาก /api/v1/account/ID/activate เป็น /api/v2/account/ID/enable อยู่ดี พูดอีกอย่างคือ ความหมายของทุก action ใน API ก็ต้องถูก hardcode ไว้ที่ไหนสักแห่งอยู่ดี (ยังไม่นับ metadata เสริมอย่างไอคอน คำอธิบาย action ที่ต้องแปล ฯลฯ ที่ก็มักไม่พอ) ความเป็น "ทั่วไป" ของวิธีนี้จริง ๆ แล้วเป็นภาพลวงตา
เมื่อ 13 ปีก่อนตอนที่ผมต้องพัฒนา HTTP API ครั้งแรก ผมอ่านวิทยานิพนธ์ของ Fielding ตั้งแต่ต้นจนจบเพื่อทำความเข้าใจว่า REST ที่แท้จริงคืออะไร และยังอ่าน RESTful Web Services Cookbook ด้วย จากนั้นก็พยายามทำ REST API โดยเลี่ยงแนวทางปฏิบัติของ Django นี่เป็นวิธีแบบ ‘cargo cult’ อยู่พอสมควร เพราะผมเองก็ไม่แน่ใจว่า REST ที่แท้จริงให้ประโยชน์อะไรกับบริการของเรา หลังจากนั้นอีกหลายปีที่ได้สร้าง HTTP API หลากหลายแบบ ผมก็ได้ข้อสรุปว่าในงานจริง ข้อดีตามทฤษฎีของ REST แทบไม่มีเลย วิสัยทัศน์เรื่อง "self-discovery" และ "เข้ากันได้กับ generic client" แทบเป็นไปไม่ได้ และในเชิงรูปธรรม วิทยานิพนธ์ของ Fielding เองก็ไม่ได้ให้แนวทางสมบูรณ์ในจุดนี้ด้วย ถ้าจะทำ API ที่ค้นพบตัวเองได้จริง คุณต้องมีกฎชัดเจนเกี่ยวกับ "endpoint discovery protocol", "คำอธิบาย operation", "ข้อความช่วยเหลือ" ฯลฯ และสุดท้ายก็ต้องสร้างไคลเอนต์เฉพาะที่เข้าใจกฎพวกนั้นอยู่ดี ซึ่งทำให้ข้อดีของ generic client หายไป สรุปคือ ในโลกจริงก็หนีไม่พ้นการเขียนโค้ดเฉพาะเซิร์ฟเวอร์ ไม่ว่าจะเป็น service-specific API, โค้ด JS, CLI ฯลฯ และ UX ที่ดีเองก็มักขัดกับอุดมคติของ REST เพราะถ้าจะให้ UX ดีจริง ก็ต้องมีโค้ดเฉพาะแอปอยู่ฝั่งฟรอนต์เอนด์ จะพยายามทำมาตรฐานให้ UI element ก็ได้อยู่ แต่ในทางปฏิบัติการสร้าง UI แบบยืดหยุ่นผ่านภาษาอย่าง JavaScript มีประโยชน์กว่ามาก
เห็นด้วยกับข้อจำกัดของแนวคิด API ที่ค้นพบตัวเองได้ ไคลเอนต์ REST ที่แท้จริงนั้นแทบจะสร้างไม่ได้จริง เพราะมันต้องรู้พฤติกรรมของทุก URL และถ้ามีพฤติกรรมใหม่เพิ่มเข้ามา (เช่น /cansofspam/123/frobnicate) ไคลเอนต์ก็ไม่อาจจัดการได้อย่างชัดเจน สุดท้ายก็ต้องอัปเดตไคลเอนต์ ไม่ก็เมินมัน หรือเพิ่มได้แค่ปุ่มเรียบง่ายมาก ๆ (เช่น Frobnicate) เพราะงั้นจึงแทบไม่มีทั้ง REST server หรือ client ที่สมบูรณ์ในความหมายเต็ม ๆ ในโลกจริง สิ่งที่ใช้ได้จริงคือให้ไคลเอนต์รองรับเฉพาะ API ที่คาดหวังไว้ล่วงหน้าโดยไม่ต้องมี discovery
API มีหลายมิติ เลยอธิบายได้ยาก ผู้ใช้ API ต้องรู้ด้วย เช่น ค่าหน่วงเวลาเฉลี่ยของการตอบสนอง, error code ที่ retry ได้, ความเป็น atomic/idempotent ของ action เป็นต้น HATEOAS บอกเรื่องพวกนี้ไม่ได้ ต่อให้ไม่ต้องทำ REST แบบสมบูรณ์ ข้อดีพื้นฐานของ REST ก็มีแค่ว่ามันให้ภาษากลางสำหรับแมปคำนาม/คำกริยาเข้ากับ HTTP method และ URL เท่านั้น ถึงอย่างนั้นก็ยังมีรายละเอียดการออกแบบที่ต้องคิดอีกเยอะ เช่น บางอย่างที่ HTTP spec อนุญาต แต่ใช้จริงบน LB ไม่ได้ หรือเกณฑ์การ retry 500 error / ตรรกะ backoff ที่ต้องพิจารณา
เบราว์เซอร์ต่างหากคือ "generic code" และมอบ UX ที่ดีที่สุดที่เราใช้กันทุกวันอยู่แล้ว ในแนวคิด REST นั้นรวมถึงกรณีที่เซิร์ฟเวอร์ส่งโค้ดให้ไคลเอนต์ด้วย (แม้จะมีประเด็นด้านความปลอดภัย แต่เบราว์เซอร์และมาตรฐานต่าง ๆ ก็แก้เรื่องนี้ไปมากแล้ว) ลิงก์ส่วนที่เกี่ยวข้องในวิทยานิพนธ์ของ Fielding
ที่จริงแม้แต่นิยาม REST ฉบับอ่อนลงก็แทบไม่มีประโยชน์อะไรนัก เรื่องอย่าง "ต้องใช้ DELETE เพื่อลบ resource" ไม่ได้สำคัญขนาดนั้น ใช้ POST แล้วจะเดือดร้อนอะไรล่ะ?
ผมไม่เคยมองว่า 'self-discoverable' เป็นเป้าหมาย และก็ไม่คิดว่ามันเป็นสิ่งที่ทำได้จริงด้วย สำหรับการออกแบบไคลเอนต์แบบง่าย ๆ ความคาดหวังนี้มันสูงเกินไปตั้งแต่แรก โดยเฉพาะเมื่อใน TFA เองก็ไม่มีคำว่า ‘discoverable’ ปรากฏอยู่เลย
วิธีออกแบบ API แบบนี้มีประโยชน์จริงในกรณีที่ผู้ใช้และ agent ที่ทำหน้าที่สำรวจแทนผู้ใช้ (เช่น เบราว์เซอร์) สามารถสำรวจ API และโต้ตอบตาม media type และข้อมูลลิงก์ในแต่ละ response ได้ แต่ web API ส่วนใหญ่ไม่ได้มี use-case แบบนั้น กลับถูกออกแบบมาสำหรับเว็บแอปที่ต้องการ UI/UX เฉพาะเจาะจง ซึ่งนี่เป็นทิศทางที่เลือกโดยตั้งใจ ผู้สร้างแอปต้องการควบคุมการนำเสนอข้อมูล ลำดับการไหลของ UI ฯลฯ อย่างเต็มที่เพื่อให้ตรงกับเป้าหมายของแอป การออกแบบแบบ REST API จึงเหมาะเมื่อผู้ใช้ควรมีอำนาจควบคุมวิธีใช้ resource ของ API มากกว่า ตัวอย่างเช่น
จริง ๆ แล้วเอกสาร HTML ก็คือตัวอย่างนั้นพอดี ในเอกสารมีลิงก์ไปยังเอกสารอื่น และผู้ใช้ก็สำรวจไปทางไหนก็ได้ตามข้อความที่อยู่บนลิงก์ ถ้าทำเพื่อผู้ใช้เราเรียกมันว่า UI แต่ถ้าทำเพื่อแอปพลิเคชันเราก็เรียกว่า API เหตุผลที่ HATEOAS ดูแปลกก็เพราะมันเหมือนพยายามทำให้ API เป็นมิตรกับผู้ใช้โดยตรง ทั้งที่เราได้ประโยชน์แบบนั้นอยู่แล้วในรูปของ UI
แนวคิด REST แบบบริสุทธิ์เป็นอะไรที่วิชาการมาก ในโครงการ open data / big data ถ้าจะสร้างประสิทธิภาพหรือสถาปัตยกรรมที่ใช้ได้จริง การมองแบบภาคปฏิบัติสำคัญกว่าการจะบรรลุ REST หรือไม่ แม้แต่นักวิชาการเองสุดท้ายก็ต้องสร้างของที่ใช้งานได้ จึงไม่ได้ยึดติดกับ REST สมบูรณ์แบบเพียงอย่างเดียว
การออกแบบ API ประเภทนี้มีประโยชน์ไม่ใช่แค่กับหน้าเว็บ แต่ยังมีประโยชน์เวลาเราสร้างไคลเอนต์แบบอื่นด้วย เช่น GET resource มาแล้วดึงค่าจาก field/path จากนั้นสร้าง URI ใหม่เพื่อจัดการต่อ ฯลฯ เราสามารถใช้แพตเทิร์นคล้ายกันนี้สร้างแอป, CLI, UI ได้หลากหลาย ถ้าเป็น non-SPA ก็อาจทำด้วย HTML ตรง ๆ ได้เลย สุดท้ายก็คือผู้ใช้ (หรือ user-agent) ทำการ dereference ข้อมูลภายใน representation ที่ส่งกลับมา
ถ้ายุคที่ AI เป็นผู้บริโภค API มาถึง use-case นี้อาจสำคัญขึ้นก็ได้ ความ discoverability ของ API มีประโยชน์กับ AI มากกว่านักพัฒนาเว็บแอปอย่างมาก แค่ดู MCP (โปรโตคอลควบคุมแบบ immersive) ก็จะเห็นว่า tool discoverability ทรงพลังแค่ไหน HATEOAS อาจมีข้อดีเชิงศักยภาพมากกับการใช้ bare API แบบนี้ด้วย
ลิงก์เอกสาร RESTful service API ของหน่วยงานอุตุนิยมวิทยาแห่งสหรัฐฯ เป็นตัวอย่างที่ดีว่าถ้า API ข้อมูลสาธารณะออกแบบมาดี มันใช้งานสบายมากจริง ๆ
สำหรับประเด็นที่ว่า "ภาระทางความคิดเริ่มต้นในการสร้างไคลเอนต์แบบ hypermedia เต็มรูปแบบนั้นมหาศาลมาก และการ hardcode URI template (/users/{id}/orders ฯลฯ) ดูง่ายกว่ามาก" ผมก็รู้สึกจากประสบการณ์ตรงว่าใช่เลย มันง่ายกว่าจริง หลักการ REST แบบดั้งเดิมให้ผลตอบแทนต่อความคุ้มค่าต่ำในสถานการณ์ส่วนใหญ่ เหมือนเตาไมโครเวฟที่ใช้ปุ่มเดียวควบคุมทั้งเมนู วิธีทำงาน และเวลา ซึ่งยุ่งยากกว่าการใช้ปุ่มมาตรฐานที่เราคุ้นเคยมาก เครื่องอ่านโค้ดเครื่องยนต์แบบ 2 ปุ่มที่ผมใช้อยู่จริงก็ใช้งานลำบากแบบน่าหงุดหงิด วัฒนธรรมที่ยังบอกว่าต้องอ่านวิทยานิพนธ์ของ Fielding ให้ได้จึงเป็นเรื่องที่น่าถกเถียงไม่น้อย ถ้ามันเป็นไอเดียที่ดีจริง ก็ควรอธิบายให้เข้าใจง่ายได้หลายแบบและในมุมมองที่คนทั่วไปเข้าถึงได้ เช่น ไม่มีใครบอกว่าถ้าจะเข้าใจฟิสิกส์ต้องไปอ่าน Principia ของ Newton เท่านั้น
ถ้าจะนำแพตเทิร์น RESTful/HATEOAS มาใช้ให้มีคุณค่าจริง ก็ต้องมีไคลเอนต์ที่เข้าใจมันด้วย htmx: hypermedia clients intercoolerjs: hatoeas-is-for-humans
นักออกแบบ UI อยากควบคุมรายละเอียดหน้าตาในแต่ละหน้าจออย่างใกล้ชิด บาง action ที่ทำได้กับ resource อาจถูกทำเป็นปุ่มใหญ่ บาง action อาจซ่อนไว้ในเมนู หรือไม่แสดงใน UI เลยก็ได้ ถ้า action ถูกเรนเดอร์แบบไดนามิกจาก response ของ API ตามแต่ละ state ทุก action ก็จะหน้าตาเหมือนกันหมด เพราะงั้นผมมองว่า RESTful API ไม่เหมาะกับการทำ UI เว็บฟรอนต์เอนด์แบบทั่วไป
ข้ออ้างนี้มีข้อผิดพลาดหลายจุด
จากประสบการณ์ของผม งานพัฒนา "RESTful API" ไม่ค่อยเกี่ยวข้องกับ UI โดยตรงนัก ถ้าต้องการแค่ UI จริง ๆ ก็ไม่จำเป็นต้องมี API เลย ใช้วิธี server-driven แบบตรง ๆ (เช่น DWR สมัยก่อน) ก็ได้
HATEOAS ดูเหมือนแทบไม่ถูกใช้จริงในโลกความเป็นจริง ผมเลยไม่ค่อยเข้าใจว่าทำไมยังมีการถกกันอยู่มากขนาดนี้ อยากรู้จริง ๆ ว่ามีที่ไหนใช้บ้าง และมี "ไคลเอนต์สำรวจอัตโนมัติ" แบบไหนที่ไม่ต้องรู้ข้อมูลของเซิร์ฟเวอร์ล่วงหน้าจริงหรือเปล่า
ขอเตือนว่า ACME (โปรโตคอลของ Let’s Encrypt) สร้างบน HATEOAS ซึ่งหมายความว่ามันถูกใช้โดยบริการ HTTPS แทบทั้งหมด HTTP เองถ้าใช้ตามรูปแบบดั้งเดิมก็เป็นโปรโตคอล HATEOAS ด้วย "auto-discovery" หมายถึงการสำรวจ resource ผ่าน link type หรืออย่าง ‘next’ เป็นต้น แน่นอนว่าไคลเอนต์ยังต้องรู้ความหมายของ "next" ล่วงหน้าอยู่บ้าง แต่ LLM เองก็เก่งกับการสำรวจอัตโนมัติแบบนี้
ผมเคยใช้ HATEOAS ในระบบเฝ้าระวังวิดีโอระดับองค์กร มันช่วยแก้ปัญหาเรื่องเวอร์ชันและสิทธิ์ได้ดีมากในระดับ API และยังใช้หลาย RFC ร่วมกันด้วย แต่ปัญหาอันดับหนึ่งคือคนมักไล่ตาม "ความสะดวก" ด้วยวิธีที่พังโมเดลและยิ่งเพิ่มความซับซ้อน อีกทั้ง JSON โดยธรรมชาติไม่ใช่ฟอร์แมตไฮเปอร์เท็กซ์ พอพยายามยัด HATEOAS ลงใน application/json มันเลยดูฝืน ๆ
คุณกำลังใช้ HATEOAS เพื่อพิมพ์คอมเมนต์นี้อยู่ และตอนนี้ก็กำลังตอบกลับอยู่ด้วย "ไคลเอนต์สำรวจอัตโนมัติอันมหัศจรรย์" ที่จัดการสิ่งนี้ก็คือ "เว็บเบราว์เซอร์" นั่นเอง
htmx อาจเป็นความพยายามที่ใช้งานได้จริงที่สุด
มาตรฐานอย่าง OData ก็แทบไม่มีใครใช้ และก็ไม่ได้ popular มากนัก HATEOAS ยิ่งขาดทั้งความนิยมและความเป็นมาตรฐาน จึงยิ่งไม่ค่อยขยายวง
สิ่งที่มักถูกมองข้ามในการถกเถียงนี้คือประเภทของผู้บริโภค backend API REST และ HATEOAS มักมีความหมายมากขึ้นเมื่อผู้ใช้เป็น third party ที่ไม่ได้เป็นเจ้าของ backend โดยตรง ตัวอย่างเช่น ผู้บริโภคปลายทางของหน้า HTML แบบดั้งเดิมคือผู้ใช้เบราว์เซอร์ ส่วน MCP ที่เกิดขึ้นในช่วงหลังก็เกิดมาเพื่อกรณีที่ต้องมี "การค้นพบและการตีความ" กับ JSON RPC API หลากหลายแบบ ในทางกลับกัน ถ้าฟรอนต์เอนด์กับแบ็กเอนด์จับคู่กันแบบ 1:1 ข้อดีของ REST มักไม่คุ้มกับต้นทุน เพราะต้องทำเอกสาร/สเปกให้ generic ขึ้นมาก และในงานจริง เครื่องมือที่จงใจมองข้าม separation of concerns เพื่อเพิ่มผลิตภาพ (เช่น trPC) กลับมีประโยชน์กว่าบ่อยครั้ง ตอนทำโปรโตไทป์ การเชื่อมต่อ end-to-end ก็เร็วกว่า
เห็นด้วยมาก และขอแนะนำบทความ HATOEAS is for humans กับ htmx: hypermedia clients
*HATEOAS
สำหรับข้ออ้างที่ว่า HATEOAS กับ schema reference (เช่น XSD, JSON Schema ฯลฯ) สามารถทำให้เกิดไคลเอนต์สำรวจแบบไดนามิกได้ ในโลกจริงความสามารถอย่าง "additionalProperties" ของ JSON schema กลับยิ่งพาเราย้อนกลับไปติดปัญหาเดิม ผมคิดว่าการส่งเอกสารแบบ "out of band" แข็งแรงกว่า ถ้าอย่างนั้น ถ้าใส่แค่ลิงก์เอกสาร Swagger หนึ่งอันไว้ใน "_links" และให้ไคลเอนต์ไปจัดการข้อมูล self path เอง จะเป็นอย่างไร? ถ้าสุดท้ายเป็นแบบนั้น แล้ว "_links" จะมีไว้ทำไม? ถ้ามีไคลเอนต์ที่จัดการเอกสาร JSON ซับซ้อนแบบนั้นได้อยู่แล้ว การใช้ Swagger template ฯลฯ ก็จะให้ข้อมูลหนาแน่นกว่าและไดนามิกกว่ามาก แค่ลิงก์ CRUD อย่างเดียวไม่พอจะอธิบาย API ทั้งระบบได้ และ JSON schema เองก็ครอบคลุมทุกอย่างไม่ได้ด้วย
เรียกมันว่า HTTP API เฉย ๆ ทุกคนจะสบายใจกว่า REST เองแต่เดิมก็ไม่ได้ถูกออกแบบมาเพื่อ API ตั้งแต่แรก มันถูกออกแบบมาสำหรับระบบสารสนเทศที่มนุษย์รับข้อมูล ไม่ใช่สำหรับโปรแกรม