แนวออกแบบ RESTful API — วิธีปฏิบัติที่ดี
เว็บไซต์ใหญ่ๆ ทั้งหลาย อย่างเช่น Google, Facebook, Twitter หรือ LINE ก็ได้เปิดเผย API ออกมาให้ Developer อย่างเราได้เขียนโปรแกรมเพื่อเชื่อมต่อกับบริการของผู้ให้บริการต่างๆ ของเว็บไซต์เหล่านั้น
แม้ว่าเราไม่ต้องเขียน API ให้ Developer ได้ใช้ แต่ก็เป็นการดีถ้าแอพพลิเคชันของเรามี API สวยๆ ไว้เผื่อให้อนาคตอาจจะมีเหล่า Developer มาใช้งานบ้าง
มีการถกเถียงมากมายในอินเตอร์เน็ตว่า วิธีไหนเป็นวิธีการที่ดีที่สุด ในการออกแบบ API โดยที่ไม่มีแนวทางออกแบบอย่างเป็นทางการ และบทความนี้ก็เป็นหนึ่งวิธีที่เหมาะสมอย่างยิ่ง หรือเป็นวิธีปฏิบัติที่ดี
API ก็ย่อมาจาก Application Programming Interface เป็นส่วน interface ที่เชื่อมแอพพลิเคชันกับภายนอก และใช้โดย Developer เพื่อใช้ข้อมูลและเข้าถึงข้อมูล ดังนั้นการออกแบบ API ที่ดีจะทำให้ใช้งานได้ง่าย ซึ่งจะทำให้ชีวิต Developer ราบรื่นขึ้น โดยที่ API เปรียบเสมือน GUI สำหรับ Developer ถ้ามันใช้ยุ่งยาก Developer ก็หาวิธีอื่นในการใช้งานหรือเลิกใช้งานไปเลย ดังนั้นประสบการณ์ของ Developer เป็นตัวสำคัญที่จะวัดคุณภาพของ RESTful API
API เหมือนกับนักดนตรีทำการแสดงบนเวที และ User ก็คือผู้ฟัง
คำศัพท์ที่สำคัญที่เกี่ยวกับ REST API
Resource เป็น object หรือเป็นตัวแทนของบางสิ่งบางอย่าง ซึ่งมีการเชื่อมโยงกับข้อมูล ซึ่งสามารถ set เป็น method ที่จะกระทำอย่างใดอย่างหนึ่งกับข้อมูล เช่น Animals, Schools และ Employees เป็น resource และ GET, POST, PUT และ DELET เป็น Method หรือวิธีการที่จะกระทำกับ Resource
Collections เป็นเซตของ Resource เช่น Companies เป็น collection ของ resource ของ company
URL ย่อมากจาก Universal Resource Locator เป็น path หรือเส้นทางไปยังที่อยู่ของ resource และบาง action ที่จะกระทำกับ resource
API Endpoint
เรามาลองเขียน API สำหรับ companies ที่จะมี employees เพื่อที่จะทำความเข้าใจให้มากขึ้น จะมาสร้างและดูรายละเอียดของ API กัน
/getAllEmployees เป็น API ที่จะตอบสนองเป็นรายชื่อของพนักงานทั้งหมด และ API อื่นๆ ของ companies ก็มีดังนี้
- /addNewEmployee (เพิ่มพนักงานใหม่)
- /updateEmployee (อัพเดตข้อมูลพนักงาน)
- /deleteEmployee (ลบพนักงาน 1 คน)
- /deleteAllEmployees (ลบพนักงานทั้งหมด)
- /promoteEmployee (เลื่อนขั้นพนักงาน 1 คน)
- /promoteAllEmployees (เลื่อนขั้นพนักงานทั้งหมด)
และอาจจะมี API อีกมากมายตามวิธีการหรือสิ่งที่ต้องการทำกับ resource โดยอาจจะมีหลาย API ที่มี action มากเกินความจำเป็น ซึ่งจะทำให้ API มีความยากลำบากในการบำรุงรักษา เมื่อจำนวนของ API เพิ่มมากขึ้น
มีอะไรผิด?
URL ควรจะมีแค่ resource (คำนาม) และไม่ควรจะมีคำของ action หรือ verbs (คำกิริยา) แต่ API path ที่สร้างมานี้ /addNewEmployee มีคำของ action คือ addNew ตามด้วย Resource คือ Employee
แล้วอย่างไรถึงจะถูก?
/companies เป็น endpoint ที่ถูกดีสำหรับตัวอย่างนี้ ซึ่งไม่มีคำของ action แต่คำถามคือเราจะมีวิธีการใดที่จะบอก server ว่าจะมี action อะไรบ้างที่จะต้องทำกับ resource companies แล้วจะ add, update หรือ delete ได้อย่างไร?
เราจะใช้ HTTP methods (GET, POST, PUT, DELETE) หรือบางทีเรียกว่ากริยาของ HTTP จะทำหน้าที่ action กับ resource companies
resource ควรจะใช้คำพหูพจน์ ใน API endpoint และถ้าเราต้องการจะเข้าถึงบางข้อมูลของ resource เราก็สามารถส่ง id เข้าไปใน URL ได้ ตัวอย่างเช่น
- method GET path /companies จะได้รายการของ company ทั้งหมด
- method GET path /companies/34 จะได้รายละเอียดของ company 34
- method DELETE path /companies/34 จะเป็นการลบ company 34
และในบางกรณีถ้าเรามี resource ถายใต้ resource อีกที ตัวอย่างเช่น Employee ของ Company จะได้ API enpoint ดังนี้
- GET /companies/34/employees จะได้ employee ทั้งหมดของ company 34
- GET /companies/34/employees/99 จะได้รายละเอียดของ employee 99 ของ company 34
- DELETE /companies/34/employees/99 เป็นการลบ employee 99 ของ company 34
สรุป: path ของ URL ควรจะเป็นรูปพหูพจน์ของ resource และ HTTP method ควรใช้สำหรับกำหนด action ที่จะทำกับ resource นั้นๆ
HTTP method (คำกริยา)
HTTP ได้กำหนดเซตของ resource ซึ่งจะบ่งบอกชนิดของ action ที่จะกระทำกับ resource
URL เป็นประโยค, ซึ่ง Resource เป็นคำนามและ HTTP method เป็นคำกริยา
HTTP method ที่สำคัญมีดังนี้
- GET เป็น method สำหรับร้องขอข้อมูลจาก resource และจำไม่มี side effect . เช่น /companies/34/employees จะตอบสนองเป็นรายการ employee ของ company 34
- POST เป็น method สำหรับสร้างข้อมูลใหม่ใน resource ซึ่งจะส่งข้อมูลใน body. เช่น /companies/34/employees จะสร้าง employee ใหม่ของ company 34 โดยที่ POST เป็น non-idempotent หมายความว่าหลายๆ request จะมีผลที่แตกต่างกัน
- PUT เป็น method สำหรับอัพเดตข้อมูลของข้อมูลที่มีอยู่แล้วใน resource หรือสร้างใหม่ เช่น /companies/34/employees/john จะอัพเดตหรือสร้างข้อมูลของ john ใน resource employees ของ company 34 โดยที่ PUT เป็น idempotent หมายความว่าหลายๆ request จะมีผลเดียว
- DELETE เป็น method สำหรับลบข้อมูลที่มีอยู่ใน resource เช่น /companies/ 34/employees/john จะลบข้อมูล john ใน resource employees ของ company 34
HTTP response status code (รหัสสถานะ)
เมื่อ client สร้าง request ไปยัง server ผ่าน API, ควรจะรู้ผลสะท้อนกลับ ไม่ว่าการ request นั้น จะ Pass, Fail หรือ การ request นั้นผิด โดยที่ HTTP status จะเป็นรหัสมาตรฐานที่บอกสถานะของการ request ซึ่งจะมีหลากหลายในแต่ละสถานการณ์ ซึ่ง ตัว Server เองควรจะ return status code ให้ถูกต้อง
ต่อไปนี้จะเป็น HTTP status code ที่สำคัญโดยแบ่งออกเป็นหมวดดังนี้:
2xx (หมวด Success)
เป็น status code ที่บอกว่าการ request นั้นได้รับแล้วและกระทำตาม method สำเร็จโดย Server
- 200 Ok เป็นมาตรฐานของ HTTP response นั้น Success สำหรับ GET, PUT หรือ POST
- 201 Create เป็น response สำหรับข้อมูลใหม่ได้ถูกสร้างขึ้น ใช้สำหรับ POST
- 204 No Content เป็น response สำหรับ request ที่ดำเนินการ Success แต่ไม่ได้ return ข้อมูลกลับ
DELETE เป็นตัวอย่างที่ดี, DELETE /companies/34/employees/john จะ request ให้ลบ John ใน employees และใน return เราไม่จำเป็นต้องส่งข้อมูลกลับ เราร้องขอ System ให้ “ลบ” ข้อมูลออกแต่ถ้ามี error เกิดขึ้น อย่างเช่นข้อมูล John ไม่มีในระบบ แทนที่จะ return status code เป็น 2xx ในหมวด Success แต่จะ return status code เป็นหมวด 4xx แทน
3xx (หมวด Redirection)
- 304 Not Modified เป็น status code ที่บอกว่า client ได้รับการ response แล้วอยู่ใน cache และไม่จำเป็นจะต้องส่งผ่านข้อมูลเดิมอีกครั้ง
4xx (หมวด Client error)
โดยที่ status code เหล่านี้จะบอก client ว่าสิ่งที่ request มานั้น Failed
- 400 Bad Request บอกว่า request ที่ส่งมาโดย client นั้นไม่ถูกดำเนินการ และ Server ไม่เข้าใจว่า request เกี่ยวกับอะไร
- 401 Unauthorized บอกว่า client ไม่ได้รับอนุญาตในการเข้าถึง Resource และควรจะ request ใหม่ด้วย credential
- 403 Forbidden บ่งบอกว่า request นั้นถูกต้องและ client ได้รับการอนุญาต แต่ Client ไม่ได้รับการอนุญาตให้เข้าถึง Resource หรือหน้าเพจด้วยเหตุผลบางประการ เช่น บางครั้ง Client ที่ได้รับอนุญาต ไม่อนุญาตให้เข้าถึงระบบไฟล์
- 404 Not Found บ่งบอกว่า resource ที่ request มานั้น ไม่ว่างใช้งานตอนนี้
- 405 Gone บ่งบอกว่า resource ที่ต้องการนั้นไม่มีอยู่แล้ว หรืออาจจะย้ายไปที่อื่น
5xx (หมวด Server error)
- 500 Internal Server Error บ่งบอกว่าการ request นั้นถูกต้อง แต่ server มีความสับสนและจะบริการด้วยเงือนไขที่คาดการไม่ได้
- 503 Service Unavailable บ่งบอกว่า server ใช้การไม่ได้ หรือไม่ว่างที่จะรับและดำเนินการ request โดยส่วนใหญ่แล้ว server อยู่ในช่วงบำรุงรักษา
ระเบียบแบบแผนในการตั้งชื่อ
เราสามารถจะใช้แบบแผนในการตั้งชื่อ หรือว่า naming convention ได้ แต่ต้องให้แน่ใจว่าเราใช้แบบแผนเดียวกันทั้งหมดตลอดทั้ง API ถ้า request body หรือ response เป็น JSON ดังนั้นควรใช้ camelCase ให้เหมือนกัน
ค้นหา, จัดเรียง, กรองข้อมูล และ จัดหน้า
action ทั้งหมดดังที่กล่าวมาแล้วนั้นเป็นแบบง่ายสำหรับการดำเนินการกับข้อมูล แต่ถ้าเราต้องการค้นหา, จัดเรียง, หรือกรองข้อมูล ทำได้โดยต่อท้าย URL ด้วย query parameter ซึ่งจะอธิบายต่อไปนี้
- Sorting ในกรณีของการจัดเรียงนี้ client ต้องการรายการของบริษัทที่ถูกจัดเรียง โดย GET /companies ควรจะรับหลายๆ sort params ใน query เช่น GET /companies?sort=rank_asc ซึ่งจะได้รายการที่ถูกจัดเรียงตามลำดับจากน้อยไปหามาก
- Filtering สำหรับกรองข้อมูล เราสามารถส่งหลากหลาย ผ่านทาง query params เช่น GET /companies?category=banking&location=thailand จะได้ข้อมูลที่ถูกกรองที่เป็นบริษัทหมวดธนาคารที่อยู่ในประเทศไทย
- Searching เมื่อเราต้องการค้นหาชื่อของบริษัท เราสามารส่งผ่านทาง query params เช่น GET /companies?search=Digital Mckinsey
- Pagination เมื่อข้อมูลมีขนาดใหญ่ เราจะย่อยข้อมูลออกมาเป็นก้อนเล็กๆ ซึ่งจะช่วยเพิ่มประสิทธิภาพและง่ายต่อการจัดการ response เช่น GET /companies?page=23 หมายความว่าจะได้รายการของบริษัทหน้า 23
การจัดการเวอร์ชัน
เมื่อ API ของเราถูกใช้ในวงกว้างแล้ว หรือมี Developer ใช้งานแล้ว การจะอัพเกรด API ด้วยการ breaking change นั้น มีโอกาสสูงมากที่จะทำให้ product หรือ service ที่ใช้ API อยู่ใช้งานไม่ได้ ดังนั้น
http://api.yourservice.com/v1/companies/34/employees จึงเป็นตัวอย่างที่ดี ที่เราจะมีเวอร์ชันของ API อยู่ใน path ของ URL ถ้ามีการ backing change เราจะต้องเปลี่ยนเวอร์ชันของ API ไปเป็น v2 หรือ v1.x.x
ทั้งหมดนี้เป็นเวอร์ชันแปลของบทความ RESTful API Designing guidelines — The best practices ถ้าผิดพลาดประการใดสามารถแนะนำได้ครับ