หลักเกณฑ์และแนวทางปฏิบัติที่ดีที่สุดสำหรับการออกแบบ RESTful API
Table of contents
Intro
ปัจจุปันการพัฒนา API หรือ Microservice เพิ่มขึ้นอย่างมีนัยสำคัญ ซึ่งมีส่วนสำคัญในการใช้แลกเปลี่ยนข้อมูล กรณีมีแอปพลิเคชันในหลายรูปแบบเช่น Web app หรือ Mobile app และ IoT โดยใช้ Restful API เป็นตัวกลางเชื่อมต่อระหว่างแต่ละอุปกรณ์ซึ่งอนุญาตให้แลกเปลี่ยนข้อมูลระหว่าง Server กับ Client โดย Server รับ Requests จาก Client และส่ง Response ข้อมูลกลับ
API
คำว่า API นั้นย่อมาจาก Application Programming Interface ซึ่ง API จะเป็นตัวกลางที่อนุญาตให้แอปพลิเคชันแลกเปลี่ยนข้อมูลผ่าน Endpoint โดย Client จะใช้ API ส่งคำร้องขอ (Request) และตอบกลับข้อมูล (Response) (คล้าย ๆ การสื่อสารระหว่าง 2 อุปกรณ์โดยมี API เป็นตัวกลาง)
Endpoints คืออะไร ? Endpoint คือ Term ของ URL Path เป็นเหมือนภาษาพูดที่ใช้เรียก Path ของ API โดยจะเรียก URLs ทั้งหมดว่า API แต่ละ URL จะเรียกว่า Endpoint
Public endpoints คือ Endpoint ที่สามารถเข้าถึงได้แบบสาธารณะ ไม่จำเป็นต้องมีการยืนยันตัวตนก่อนใช้งาน
Authenticated endpoints คือ Endpoint ที่จำเป็นต้องมีการยืนยันตัวตนก่อนใช้งาน
REST
คำว่า REST นั้นย่อมาจาก Representational State Transfer ซึ่ง Rest คือคำเรียกในเชิงแบบสถาปัตยกรรมที่ใช้ในการพ ัฒนาเว็บเซอร์วิส โดยนำเสนอครั้งแรกโดย Roy Fielding https://en.wikipedia.org/wiki/Roy_Fielding
RESTful API
RESTful นั้นพัฒนามาจากรากฐานสถาปัตยกรรมรูปแบบของ REST และใช้ HTTP Methods เรียกว่า RESTful web services
โดยทั่วไป API นั้นช่วยให้การแลกเปลี่ยนข้อมูลนั้นง่ายขึ้นมาก ๆ โดยจะอนุญาตให้นักพัฒนา Integrated ฟังก์ชันบางอย่างจาก API ที่เป็น Third-party Services เพื่อสร้างฟังก์ชันอะไรบางอย่างของตัวเอง อย่างกรณีเช่น Uber และ Grab ใช้ Google Map API สำหรับการทำระบบนำทาง (Navigation) โดยช่วยให้ลดเวลาพัฒนาลงอย่างมากในการพัฒนาระบบเองจากศูนย์
รูปภาพ อธิบายการทำงานของ API
RESTful API Design
API นั้นสามารถสร้างได้จากหลากหลายภาษา (Server side script Language Programming) ไม่ว่าจะเป็น PHP Ruby JS Java Python และ Golang เป็นต้น
คำศัพท์
- Collections Resource เช่น /api/v1/users สำหรับหลาย items
- Instance Resource เช่น /api/v1/users/123 สำหรับ item เดียว
โครงสร้าง API Endpoint อ้างอิงตามด้านล่างนี้ โดย URL โครงสร้างควรเป็นไปตามแบบลำดับชั้น และสามารถคาดการณ์ได้เพื่อให้ทำความเข้าใจและการใช้งานง่าย
https://api.abc.com/v1/companies/123/users/?attributes=first_name,last_name
\___/ \_________/\_/\__________________/\_______________________________/
| | | | |
scheme base version path query parameters
รายละเอียด
https://api.abc.com/v1/shops/3/categories/5/toys/2/parts?material=plastic
\___/ \_________/\_/\____/\_/\________/\_/\__/\_/\___/\_______________/
| | | | | | | | | | |
scheme base 0 1 2 3 4 5 6 7 8
0. API Version
1. All shops
2. Shops with id = 3
3. All categories
4. Category with id = 5
5. All toys
6. Toy with id = 2
7. All parts
8. Query parameters (Optional) using with sorting, filtering, pagination
รูปแบบของ Resource Names ที่ดี
- ควรเป็นคำนาม อย่าใช้คำกริยาในการอธิบาย
- ชื่อ Resource ควรเป็นเอกพจน์สำหรับแบบ Instance Resource
- ชื่อ Resource ควรเป็นพหูพจน์สำหรับแบบ Collections Resource
- ชื่อ Resource ควรตั้งเป็นตัวพิมพ์เล็ก (Lower-case) และเครื่องหมาย - (Hyphen) สำหรับคั่นคำ
รูปแบ บของชื่อ Query Parameters ที่ดี
- ตัวอักษรใน Query string ควรใช้ _ (Underscore) สำหรับคั่นคำ
- Query parameters ควรเป็นตัวพิมพ์เล็กทั้งหมด (Lower-case)
- Query parameters ใช้เท่าที่จำเป็น เช่นการ Filters และ Sorting
- Query parameters พึงระวังไว้เสมอว่าไม่ควรใช้ตัวอักษรที่เป็นข้อมูล Sensitive
รูปแบบของชื่อ Fields หรือ Attribute Names ที่ดี
- รูปแบบของชื่อควรสอดคล้องกับรูปแบบของข้อมูล JSON
- ควรเป็นตัวพิมพ์เล็กทั้งหมด (Lower-case) ใช้ _ (Underscore) สำหรับคั่นคำ
- ไม่ควรใช้ Prefix อย่างเช่น is_ หรือ has_ สำหรับ keys ประเภทแบบบูลีน
- Fields ที่เป็นรูปแบบ Arrays ควรใช้ชื่อที่เป็นคำนามพหูพจน์ ตัวอย่าง
- authenticators-contains หรือ authenticators
- products-contains หรือ products
ตัวอย่างที่ดี
พนักงานหลายคน:
GET
https://api.abc.com/v1/employees
ใช้ Plural หรือ Singular nouns (จะเลือกใช้อะไรก็ตาม ก็ควรให้มันสอดคล้องกับ APIs ทั้งหมดที่มี)
แบบก ารกรอง:
GET
https://api.abc.com/v1/employees?year=2011&sort=desc
GET
https://api.abc.com/v1/employees?section=economy&year=2011
พนักงานคนเดียว:
GET
https://api.abc.com/v1/employees/1234
สถานที่ปฏิบัติงานทั้งหมดของพนักงานของ 1234:
GET
https://api.abc.com/v1/employees/1234/locations
ระบุพารามิเตอร์ตัวเลือกสำหรับแสดงผลในรูปแบบคอมม่าลิส:
GET
https://api.abc.com/v1/employees/1234?fields=job_title,start_date
เพิ่มสถานที่ปฏิบัติงานสำหรับพนักงาน 1234:
POST
https://api.abc.com/v1/employees/1234/locations
ตัวอย่างที่ควรปรับปรุง
แบบไม่เป็นพหูพจน์:
GET
https://api.abc.com/v1/employee
GET
https://api.abc.com/v1/employee/1234
GET
https://api.abc.com/v1/employee/1234/location
มีคำกริยาใน URL:
POST
https://api.abc.com/v1/employee/1234/create
การระบุข้อมูลแบบ URL แทนที่จะเป็นการเขียนแบบ Query string
GET
https://api.abc.com/v1/employee/1234/desc
ตัวอย่างการออกแบบ CRUD API ที่ดี
ทีนี้มาดูตัวอย่างรายละเอียดสำหรับออกแบบ Article Resource API ที่ดีที่ควรจะเป็นโดยตัวอย่างจะเป็นแบบ CRUD (Create-Read-Update-Delete)
เมื่อต้องการขอข้อมูล (Read)
ที่ควรปรับปรุง
Method | URI | Description |
---|---|---|
GET | /FetchArticle | สำหรับขอข้อมูลทั้งหมด |
GET | /getAllArticles/12 | สำหรับขอข้อมูลทบางรายการ |
- อย่าใช้คำกริยาหรือพฤติกรรมเพื่ออธิบาย APIs
ที่ควรจะเป็น
Method | URI | Description |
---|---|---|
GET | /articles | สำหรับขอข้อมูลทั้งหมด |
GET | /articles/12 | สำหรับขอข้อมูลทบางรายการ |
- HTTP method ควรใช้
GET
- ใช้ Plural หรือ Singular nouns (จะเลือกใช้อะไรก็ตาม ก็ควรให้มันสอดคล้องกับ APIs ทั้งหมดที่มี)
เมื่อเพิ่มข้อมูล (Create)
ที่ควรปรับปรุง
Method | URI | Description |
---|---|---|
POST | /createarticle | สำหรับสร้างรายการข้อมูล |
GET | /createrecordforartilce | สำหรับสร้างรายการข้อมูลแบบ GET |
- URL ของ Endpoint ควรประกอบไปด้วย HTTP method ทีอธิบายความสามารถของ APIs นั้น
ที่ควรจะเป็น
Method | URI | Description |
---|---|---|
POST | /articles | สำหรับสร้างรายการข้อมูล |
- HTTP method ควรใช้
POST
- ใช้ Plural หรือ Singular nouns (จะเลือกใช้อะไรก็ตาม ก็ควรให้มันสอดคล้องกับ APIs ทั้งหมดที่มี)
เมื่ออัปเดตข้อมูล (Update)
ที่ควรปรับปรุง
Method | URI | Description |
---|---|---|
PUT | /updatearticle/12 | สำหรับการอัปเดตข้อมูลบางรายการ |
POST | /12/modifyarticle | สำหรับการอัปเดตข้อมูลบางรายการ |
ที่ควรจะเป็น
Method | URI | Description |
---|---|---|
PATCH | /articles/12 | สำหรับการอัปเดตข้อมูลบ างรายการ |
PUT | /articles/12 | สำหรับการอัปเดตข้อมูลทุกรายการ |
- HTTP method ควรใช้
PATCH
หรือPUT
(อธิบายเพิ่มเติมในหัวข้อ HTTP method) - ใช้ Plural หรือ Singular nouns (จะเลือกใช้อะไรก็ตาม ก็ควรให้มันสอดคล้องกับ APIs ทั้งหมดที่มี)
เมื่อลบข้อมูล (Delete)
ที่ควรปรับปรุง
Method | URI | Description |
---|---|---|
PUT | /deletearticle/12 | สำหรับลบข้อมูลบางรายการ |
POST | /12/deletearticle | สำหรับลบข้อมูลบางรายการ |
PATCH | /delete-article/12 | สำหรับลบข้อมูลบางรายการ |
ที่ควรจะเป็น
Method | URI | Description |
---|---|---|
DELETE | /articles/12 | สำหรับลบข้อมูลบางรายการ |
- HTTP method ควรใช้
DELETE
- ใช้ Plural หรือ Singular nouns (จะเลือกใช้อะไรก็ตาม ก็ควรให้มันสอดคล้องกับ APIs ทั้งหมดที่มี)
สรุป Routes Resource
Method | URI | Function | Route Name | Description |
---|---|---|---|---|
GET | /articles | index() | articles.index | all articles |
POST | /articles | store(req) | articles.store | create |
GET | /articles/[id] | show(id) | articles.show | show by id |
PUT/PATCH | /articles/[id] | update(id) | articles.update | edit/replace |
DELETE | /articles/[id] | remove(id) | articles.remove | remove |
HTTP methods
HTTP methods (RFC) คือ กริยา (Verb) หรือบางทีเรียก HTTP Verbs โดยเป็นการดำเนินการบนของมาตรฐาน HTTP Request ดังนี้
HTTP Method | คำอธิบาย |
---|---|
GET | สำหรับร้องขอ Resource |
POST | สำหรับเพิ่ม Resource ใหม่ หรือ สำหรับสั่งเพื่อการดำเนินการบางอย่าง เช่น POST ส่งข้อความ |
PUT | สำหรับแก้ไขข้อมูลใน Resource (ควรใช้เฉพาะกรณีต้องการ Replace ทุก Fields เท่านั้น) |
PATCH | สำหรับแก้ไขข้อมูลใน Resource (ควรใช้แก้ไขข้อมูลแค่บาง Fields เท่านั้น) |
DELETE | สำหรับลบข้อมูลใน Resource |
HEAD | สำหรับร้องขอ metadata เกี่ยวกับ Headers the request การร้องขอการตอบรับจาก Resource จะคล้ายกับ GET แต่จะไม่มีส่วนเนื้อหา (Payload) ตอบกลับ คำสั่งนี้ใช้ประโยชน์ในการตรวจสอบข้อมูลส่วนหัว โดยไม่จำเป็นต้องส่งเนื้อหาเต็มมาทั้งหมด |
OPTIONS | สำหรับร้องขอเพื่อเรียกดู HTTP method ที่สามารถใช้งานได้ จากการกำหนด CORS (cross-origin resource sharing) |
HTTP response status code
คำอธิบายและความหมายของ HTTP/1.1: Status Code Definitions (RFC)
- 2xx successful – เมื่อ Request ได้รับสำเร็จ เข้าใจ และ ยอมรับแล้ว
- 3xx redirection – เส้นทางมีการเปลี่ยนแปลง และต้องได้รับ Action อื่น ๆ เพื่อให้ดำเนินการสำเร็จ
- 4xx client error – เมื่อ request มีไวยากรณ์ที่ไม่ถูกต้อง หรือไม่สามารถทำให้สมบูรณ์
- 5xx server error – เมื่อเซิร์ฟเวอร์ไม่สามารถตอบสนองคำขอที่ถูกต้องได้
GET | POST | PUT | DELETE | PATCH | All | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Code | Status | Code | Status | Code | Status | Code | Status | Code | Status | Code | Status |
200 | OK | 201 | Created | 202 | Accepted | 202 | Accepted | 202 | Accepted | 408 | Request Timeout |
400 | Bad Request | 202 | Accepted | 204 | No content | 204 | No content | 204 | No content | 501 | Method Not Implemented |
401 | Unauthorised | 400 | Bad Request | 400 | Bad Request | 400 | Bad Request | 400 | Bad Request | ||
403 | Forbidden | 401 | Unauthorised | 401 | Unauthorised | 401 | Unauthorised | 401 | Unauthorised | ||
404 | Not found | 403 | Forbidden | 403 | Forbidden | 403 | Forbidden | 403 | Forbidden | ||
405 | Not Allowed | 404 | Not found | 404 | Not found | 404 | Not found | 404 | Not found | ||
415 | Unsupported Media Type | 405 | Not Allowed | 405 | Not Allowed | 405 | Not Allowed | 405 | Not Allowed | ||
500 | Internal Server error | 415 | Unsupported Media Type | 415 | Unsupported Media Type | 415 | Unsupported Media Type | 415 | Unsupported Media Type | ||
422 | Unprocessable Entity | 422 | Unprocessable Entity | 500 | Internal Server error | 422 | Unprocessable Entity | ||||
500 | Internal Server error | 500 | Internal Server error | 500 | Internal Server error |
Single Resource
รหัสสถานะการตอบกลับต่อไปนี้แสดงถึงการตอบกลับต่าง ๆ เมื่อการดำเนินการที่แตกต่างกันโดยสามารถทำได้บน Single resource
Request Method | Resource Path | Status | Code |
---|---|---|---|
POST | /resources | Created | 201 |
Accepted | 202 | ||
Bad Request | 400 | ||
Unauthorised | 401 | ||
Forbidden | 403 | ||
Not found | 404 | ||
Not Allowed | 405 | ||
Unsupported Media Type | 415 | ||
Unprocessable Entity | 422 | ||
Internal Server Error | 500 | ||
PUT | /resources/[id] | OK | 200 |
Accepted | 202 | ||
No content | 204 | ||
Bad Request | 400 | ||
Unauthorised | 401 | ||
Forbidden | 403 | ||
Not found | 404 | ||
Not Allowed | 405 | ||
Unsupported Media Type | 415 | ||
Unprocessable Entity | 422 | ||
Internal Server error | 500 | ||
DELETE | /resources/[id] | Accepted | 202 |
No Content | 204 | ||
Bad Request | 400 | ||
Unauthorised | 401 | ||
Forbidden | 403 | ||
Not found | 404 | ||
Not Allowed | 405 | ||
Internal Server error | 500 | ||
PATCH | /resources/[id] | Accepted | 202 |
No content | 204 | ||
Bad Request | 400 | ||
Unauthorised | 401 | ||
Forbidden | 403 | ||
Not found | 404 | ||
Not Allowed | 405 | ||
Unsupported Media Type | 415 | ||
Unprocessable Entity | 422 | ||
Internal Server error | 500 |
Collection of Resources
รหัสสถานะการตอบกลับต่อไปนี้แสดงถึงการตอบกลับต่าง ๆ เมื่อการดำเนินการที่แตกต่างกันโดยสามารถทำได้บน Collection resource (All)
Request Method | Resource Path | Status | Code |
---|---|---|---|
GET | /resources | OK | 200 |
Bad Request | 400 | ||
Unauthorised | 401 | ||
Forbidden | 403 | ||
Not found | 404 | ||
Not Allowed | 405 | ||
Unsupported Media Type | 415 | ||
Internal Server error | 500 |
Response data format
Result Messages
สำหรับการตอบกลับของข้อมูลนั้นจะมีรูปแบบดังนี้
GET /employees
จะ Returns ลิสของ Objects ในกรอบของ data
:
{
"data": [
{ "id": 1, "name": "Larry" },
{ "id": 2, "name": "Peter" }
]
}
GET /employees/1
จะ Returns แบบ Single object ในกรอบของ data
:
{
"data": { "id": 1, "name": "Larry" }
}
คำแนะนำใน Request Payload หากเป็นไปได้ทั้งแบบ PUT POST และ PATCH ควรจะมีกรอบของ
data
เพื่อบอกให้ทราบในส่วนรูปแบบของข้อมูล
ถ้าหากมีการข้ามของข้อมูลก็ควรมีการกำหนดค่าเริ่มต้น (เช่น offset=0
และ limit=100
) และไม่ควรส่งออกข้อมูลทั้งหมด ถ้าข้อมูลมีจำนวนเยอะควรจะมีการจำกัด Limit เป็นค่าเริ่มต้น (Pagination)
GET /employees # returns the employees 0 to 100
เมื่อมีการกำหนด offset และ limit
GET /employees?offset=20&limit=10
{
"pagination": {
"offset": 20,
"limit": 10,
"total": 3465,
},
"data": [
//...
],
"links": {
"next": "https://api.abc.com/v1/employees?offset=30&limit=10",
"prev": "https://api.abc.com/v1/employees?offset=10&limit=10"
}
}
ถ้าเป็นไปได้ควรมี Links ที่สามารถไปหน้าถัดไปและหน้าก่อนหน้า เมื่อ URL นั้นมีการกำหนด offset และ limit
Error Messages
เป็นส่วนที่เกี่ยวกับ Errors เมื่อมีเหตุการณ์บางอย่างที่เกิดขึ้นไม่ถูกต้องโดยในส่วนของ Body ของการตอบกลับ HTTP มีตัวอย่างนี้
Request:
GET /employees?state=super
Response:
// 400 Bad Request
{
"errors": [
{
"status": 400,
"code": "INVALID_STATE",
"message": "Invalid state. Valid values are 'internal' or 'external'",
"links": {
"about": "https://api.abc.com/v1/rest/errorcode/352"
}
}
]
}
Filtering, Sorting, Pagination
Filtering
Filtering คือความสามารถในก าร Filter และ Sort ของ Collections ใน API เพื่อที่จะอนุญาตให้ควบคุมการแสดงผลข้อมูลใน API ให้มีความยืดหยุ่น
- การกำหนดจำนวนการแสดงผลด้วยวิธีต่าง ๆ (แต่วิธีนี้ไม่แนะนำ) โดยเป็นส่วนหนึ่งของ API URI (ดัวอย่าง
/employees/age/from/20/to/30
) - Filter parameters ไม่เป็นส่วนหนึ่งของการกำหนด Resource ให้ใช้ Query parameters แทนเพื่อ Filter และ Sort ตามความต้องการ
ข้างล่างนี้จะเป็นความแตกต่างของเทคนิคที่ใช้กับ Filter และ Sort:
Output Selection
ผู้ใช้ API สามารถระบุ Attributes ที่เขาต้องการสำหรับการตอบกลับในส่วนของ Response payload ด้วยการระบุ Attributes ใน Query parameters
ตัวอย่างการตอบกลับสำหรับ first_name
และ last_name
เท่านั้น
?attributes=first_name,last_name
Simple Filtering
Attributes สามารถใช้ Filter กับ collection ของ resources
ตัวอย่าง:
?last_name=Johnson
จะ Filter ออกจาก Collection ของ resources ด้วย attribute last_name
ที่เท่ากับ Johnson
ตั วอย่าง:
?last_name=Johnson&date_of_birth=1999-12-31
จะ Filter ข้อมูลออกจาก Collection ของ resources ด้วย attribute last_name
ที่เท่ากับ Johnson
และ date_of_birth
เท่ากับ 1999-12-31
ใช้สัญลักษณ์เท่ากับ (=) สำหรับเทคนิคที่ใช้เปรียบเทียบข้อมูลที่เท่ากัน และสำหรับการดำเนินการอื่น ๆ ดูในหัวข้อ Advanced Filtering
Advanced Filtering
มีบางสถานการณ์ที่การ Filter อย่างง่ายนั้นไม่ตรงกับความต้องการและจำเป็นต้องมีแนวทางอื่นที่ครอบคลุมมากขึ้น อย่างเช่นใช้ตัวกรองสัญลักษณ์ดังต่อไปนี้เพื่อกำหนดตรรกะการกรองที่ซับซ้อนมากขึ้น
สามารถกำหนดแอตทริบิวต์หลายรายการที่มีตัวดำเนินการและเงื่อนไขต่างกันได้ในพารามิเตอร์ตัวกรองการค้นหา
รองรับตัวดำเนินการต่อไปนี้:
- >= มากกว่าหรือเท่ากับ
- > มากกว่า
- < น้อยกว่า
- <= น้อยกว่าหรือเท่ากับ
- = เท่ากับ
- != ไม่เท่ากับ
หรือไม่ว่าจะเป็นเงื่อนไข AND, OR ก็กำหนดได้
ตัวอย่าง:
?filters=creation_date >= 2001-09-20T13:00:00 and creation_date <= 2001-09-21T13:00:00 and first_name like 'Fred' and post_code=3000
จะแสดง collection ของ resources เมื่อ creation_date
อยู่ระหว่าง 2001-09-20 1pm
และ 2001-09-21 1pm
และ first-name
คือ Fred
และ post_code
คือ 3000
Match Case Sensitivity
- ตามคำแนะนำทั่วไปการ Return ข้อมูลที่คล้ายกันจะดีกว่าการไม่ส่งคืนรายการที่ไม่ตรงกัน เลย
- ค่าที่กำหนดของการกรองควรพิจารณาจะไม่คำนึงถึงขนาดตัวพิมพ์
- ไม่ว่าคุณจจะไม่คำนึงถึงขนาดตัวพิมพ์ แต่ก็ควรจัดทำเป็นเอกสารไว้อย่างชัดเจน
Sorting
การที่ข้อมูลสามารถกำหนดตามลำดับการเรียงที่เฉพาะเจาะจงได้มักเป็นความต้องการจากแอปพลิเคชันไคลเอนต์ ดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องให้ความยืดหยุ่นแก่ผู้ใช้ในการดึงข้อมูลตามลำดับที่พวกเขาต้องการ
พารามิเตอร์สองตัวที่ใช้ในการเรียงลำดับมีดังนี้:
Query Parameter | Description |
---|---|
sort | ในการจัดเรียงข้อมูลเช่น asc หรือ desc |
sort_fields | พารามิเตอร์ที่ใช้จัดเรียงเช่น id หรือ name |
sort_fields
เป็นพหูพจน์เนื่องจากมีทั้งตัวเลือกต่อไปนี้:
ตัวอย่าง:
?sort=asc&sort_fields=name,last_modified
?sort=desc&sort_fields=name&sort_fields=last_modified
คำค้นหาทั้งสองนี้ควรเรียงลำดับตามชื่อจากนั้นตามวันที่แก้ไขล่าสุด
Pagination
ระบบเลขหน้าทำตามคำ แนะนำ ดังนี้:
- ส่งคำขอในเวลาที่เหมาะสม (เช่น Request < 2 วินาที)
- ตรวจสอบให้แน่ใจว่าปริมาณข้อมูลที่ตอบกลับยังคงอยู่ในส่วนที่จัดการได้ (เช่น < 500kb)
- ตรวจสอบปริมาณข้อมูลในการตอบกลับสามารถจัดการได้ง่ายเพื่อรองรับประสบการณ์ของผู้ใช้ที่ดี (UX)
Parameters
เมื่อใช้การแบ่งหน้าให้ใช้พารามิเตอร์ต่อไปนี้ (ควรจะ):
Query Parameter | Description | Example |
---|---|---|
page | หน้าเพจที่ผู้ใช้ต้องการ | page=1 (กำหนดค่าเริ่มต้น: 1) |
limit | จำนวนผลลัพธ์ต่อหน้าที่ผู้ใช้ต้องการ | limit=10 (กำหนดค่าเริ่มต้น: 10) |
ตัวอย่างสร้ างโครงสร้าง URI ดังนี้:
Page 1: /customers?page=1&limit=10
Page 2: /customers?page=2&limit=10
...
Page 50: /customers?page=50&limit=10
โครงสร้างนี้เข้าใจง่ายเนื่องจากมีความเรียบง่าย และสามารถรวมไว้ใน UI เช่น ลิงก์ต่าง ๆ ได้โดยตรง
Common Terms
พารามิเตอร์ต่อไปนี้เป็นพารามิเตอร์ทั่วไปใน REST APIs แต่ห้ามใช้ในข้อกำหนดดังนี้
offset
และlimit
การใช้สองสิ่งนี้เป็นเรื่องปกติในฐานข้อมูล SQL และเป็นตัวเลือกที่ดีเมื่อคุณต้องการให้ผลลัพธ์ออกมาอย่า งต้องการ อย่างไรก็ตามการใช้งานพารามิเตอร์offset
อาจเป็นเรื่องยากดังนั้นจึงแนะนำให้ใช้page
แทนsince
และlimit
ดึงข้อมูลทุกอย่าง "ตั้งแต่" โดยสิ่งเหล่านี้มีประโยชน์เมื่อต้องการให้ไคลเอนต์สามารถ "ซิงค์" กับข้อมูลได้อย่างมีประสิทธิภาพ อย่างไรก็ตามโดยทั่วไปแล้วต้องมีการกำหนดการเรียงลำดับเริ่มต้นไว้ให้ชุดผลลัพธ์เพื่อให้มีความสำคัญอย่างมีนัย
Metadata
เมื่อมีการแบ่งหน้าให้กำหนดสิ่งนี้ไว้ แนะนำอย่างยิ่ง สำหรับ API เพื่อที่จะให้คำแนะนำผู้ใช้ในเนื้อหาการตอบกลับ (response body) ว่าพวกเขาอยู่ในหน้าใด ถ้าหากไม่กำหนดไว้ผู้ใช้จะต้องกำหนดให้ตรวจสอบคำขอซึ่งมักจะต้องมีโค้ดอย่างไม่จำเป็นเพื่อตรวจสอบพารามิเตอร์การสืบค้น
ตัวอย่าง:
{
"total_records" : 2340,
"_meta": {
"processing_time" : 120,
"page": 4,
"limit": 20
},
"_links" : {
"_self": "/namespace/v1/employees?page=2&limit=1",
"_prev": "/namespace/v1/employees?page=1&limit=1",
"_next": "/namespace/v1/employees?page=3&limit=1"
},
"_embedded" : {
"count" : 20,
"data" : [//...actual data here...]
}
}
API Versioning
API Versions คือส่วนที่บอกว่า APIs มีรุ่นและการเปลี่ยนแปลงอย่างไร และปัจจุบันเป็นรุ่นอะไร
ทำไมต้องมี API Versions
ทำไมต้องมี API Versions เพราะเมื่อคุณเผยแพร่ API เป็น Public แล้วดังนั้น API ของคุณจะต้องไม่เ กิดการ Breaking (อยู่ดี ๆ ใช้งานไม่ได้) ของ API โดยไม่แจ้งผู้ใช้ให้ทราบล่วงหน้า ดังนั้น API Versioning จะช่วยให้ป้องกันการเกิด Request ที่ไม่มีอยู่ เช่น จากการปรับปรุง Endpoints ดังตัวอย่างนี้
https://api.abc.com/articles
//Response result
[
{
"title": "...",
"text": "..."
}
...
]
วันดีคืนดีแก้ไข API นี้ใหม่
https://api.abc.com/articles
//Response result
{
"data": [
{
"title": "...",
"detail": "..."
}
...
]
}
จะเห็นว่า API https://api.abc.com/articles
ส่วนปรับปรุงใหม่มีส่วนที่แตกต ่างจากเดิมซึ่งจะทำให้เกิดปัญหากับ Clients ที่ใช้ API นี้อยู่โดยถ้าไม่ได้รับการปรับปรุงตาม API ใหม่ ดังนั้นเมื่อนำ API Versions มาช่วยแก้ปัญหาจะได้ดังนี้
#API Version ก่อนการแก้ไขสามารถใช้กับ Clients ได้ไม่มีปัญหา
https://api.abc.com/v1/articles
#API Version หลังการแก้ไข และรอการปรับปรุง Clients เพื่อความเข้ากันได้
https://api.abc.com/v2/articles
คำแนะนำการออกแบบ API Versions
โดยที่:
- โดย API version ควรจะ กำหนดเป็นส่วนหนึ่งใน URI
- โดย API version ควรจะ กำหนดไว้อยู่เสมอ (เช่น ค่าเริ่มเป็นเวอร์ชันล่าสุด)
- โดย API version ควรจะ ระบุด้วย 'v'
- ถ้าผู้ใช้งานพยายามเข้าถึง API ด้วยการไม่ระบุเวอร์ชัน ดังนั้นเขาก็ควรได้รับข้อความผิดพลาดตอบกลับ 404
- ถ้าผู้ใช้งานพยายามเข้าถึง API ด้วยการไม่ระบุ resource (เช่น GET .../api/v1) การตอบกลับอาจจะประกอบด้วยข้อมูลพื้นฐานเกี่ยวกับ API (เช่น ชื่อ API, และรายละเอียดอื่น ๆ)
โดยอาจจะมี API version หลายอันได้ตัวอย่าง:
- .../api/v1
- .../api/v2
ไม่ทำโครงสร้าง version ที่คล้ายกัน (ตัวอย่าง ไม่ควรมี v1.x or v1.x.y) นั่นหมายความว่าคุณมีเวอร์ชันโดยละเอียดมากเกินไป ที่บ่งบอกถึงความละเอียดของการกำหนดเวอร์ชัน แต่ใช้งานไม่ได้กับ API!
รายละเอียดของ APIs Version ควร เป็นไปตาม semantic versioning:
{MAJOR}.{MINOR}.{PATCH}
เวอร์ชันแรกของ API ต้อง เริ่มต้นด้วยเวอร์ชัน MAJOR เป็น 1 เสมอ
ใช้แนวทางต่อไปนี้เมื่อเพิ่มหมายเลขเวอร์ชัน API:
- MAJOR เวอร์ชัน เมื่อคุณทำการ incompatible หรือ breaking ของการแก้ไข API
- MINOR เวอร์ชัน เมื่อคุณเพิ่มฟังก์ชันใน backwards-compatible หรือคุณสมบัติอื่น ๆ
- PATCH เวอร์ชัน เมื่อคุณ ทำการ backwards-compatible การแก้ไขข้อพกพร่อง
APIs ทั้งหมด ควร รวมเวอร์ชัน MAJOR เป็นส่วนหนึ่งของ URI ในรูปแบบของ 'v{MAJOR}', ตัวอย่าง https://api.example.com/namespace/v1/employees
ไม่จำเป็นต้องใช้เวอร์ชัน Minor และ Patch ใน URI เนื่องจากการเปลี่ยนแปลงเหล่านี้ จะเข้ากันได้กับรุ่น Major อยู่แล้ว
โดยให้หมายเลขเวอร์ชันอื่น ๆ (Minor, Patch) จะแสดงในหน้าเอกสารประกอบ API หรือเป็นส่วนหนึ่งของการเรียกการจัดการพิเศษไปยัง URI ของ API เอง
Metadata
เพื่อให้การสนับสนุน API ของคุณ ควร ให้มีการตอบกลับคำขอ GET ไปยัง Base URI ของ API และส่งคืนรูปแบบข้อมูล Metadata ต่อไปนี้การตอบกลับ:
- api_name: ชื่อ API
- api_version: API Version กับ Major และ minor versions
- api_released: วันที่ล่าสุดที่ปรับปรุง API
- api_documentation: ลิงก์สำหรับ API Documentation
- api_status: สถานะที่บ่งบอกว่า API นั้น Active หรือ Deprecated
สามารถเพิ่มข้อมูล Metadata อื่น ๆ สำหรับการตอบกลับ (ถ้าต้องการ)
ตัวอย่าง:
//GET /namespace/v1
//HTTP 200 OK
{
"api_name": "namespace",
"api_version": "1.1.3"
"api_released": "2021-04-06"
"api_documentation": "https://api.example.com/namespace/v1/docs"
"api_status": "active"
}
คำแนะนำอื่น ๆ
แนวทางและหลักการบางประการในการตัดสินใจว่า เมื่อใดจึงจำเป็นต้องใช้ API เวอร์ชันใหม่:
- คุณไม่ควรสร้างเวอร์ชัน API ใหม่ เมื่อคุณเพิ่มฟิลด์ / แอตทริบิวต์ให้กับ Resources ที่มีอยู่ ถ้าหากผู้ใช้สามารถเพิกเฉยต่อสิ่งเหล่านั้นได้ (กล่าวคือหากผู้ใช้ต้องไม่เพิ่มสิ่งเหล่านั้นในคำขอ)
- คุณไม่ควรสร้างเวอร์ชัน API ใหม่ ถ้าหากคุณเพิ่ม Resources ใหม่ โดยที่ลูกค้าไม่จำเป็นต้องใช้มันโดยตรง
- คุณต้องสร้างเวอร์ชัน API ใหม่ ถ้าหากคุณแก้ไขทรัพยากรที่มีอยู่ในลักษณะที่จะส่งผลกระทบต่อไคลเอนต์ (เช่นฟิลด์บังคับใหม่การลบฟิลด์ ... )
- คุณต้องสร้างเวอร์ชัน API ใหม่ ถ้าหากคุณมี Resources ใหม่ โดยหากผู้ใช้ต้องได้ใช้มันด้วย
- คุณต้องสร้างเวอร์ชัน API ใหม่ ถ้าหากคุณสร้างประเภทข้อผิดพลาดใหม่
💡 Maintain เวอร์ชันอย่างน้อยหนึ่งเวอร์ชัน เพื่อให้แอปพลิเคชันอื่น ๆ ที่ใช้ API ให้มีเวลาอัปเกรดเป็นเวอร์ชันใหม่แทนที่จะทำลายทิ้งทันที
💡 สร้าง และ Maintain บันทึกการเปลี่ยนแปลง (Changelog) และคู่มือแนะนำการ Migration สำหรับ API ของคุณเพื่อให้ผู้ใช้เห็นสิ่งที่เปลี่ยนแปลงและวิธีอัปเกรดได้อย่างง่ายดาย
Documentation
เอกสาร (API Docs) เป็นสิ่งที่สำคัญสำหรับนักพัฒนาในการใช้ API โดย API ที่แตกต่างกันจะมีลักษณะการทำงานที่แตกต่างกัน ซึ่งต้องใช้พารามิเตอร์ที่แตกต่างกัน เช่น วิธีการ HTTPS การ Response data ของ API ดังนั้นนักพัฒนาส่วนใหญ่จะชอบเอกสารที่ดีสำหรับใช้อ้างอิงการใช้งาน API
มีเครื่องมือดี ๆ มากมาย เพื่อที่จะช่วยนักพัฒนาในการสร้างเอกสาร API ดังนี้:
- Swagger: ออกแบบและสร้างโมเดล API ตามมาตรฐานตามข้อกำหนด เพิ่มประสบการณ์ (UX) ของนักพัฒนาด้วยเอกสาร API แบบโต้ตอบได้
- Slate: Slate ช่วยให้คุณสร้างเอกสาร API ที่สวยงาม ฉลาด ความสามารถการตอบสนอง ที่สำคัญ Slate คือโครงการโอเพ่นซอร์สและใช้งานได้ฟรี
Security Features
เป็นที่นิยมในการใช้คุณสมบัตินโยบายความปลอดภัยที่มีอยู่ในแพลตฟอร์ม API Gateway แทนที่จะใช้นโยบาย (Policy) การเข้าถึงใน back-end API ตรง ๆ แต่วิธีใดก็ตามควรจะมีรูปแบบความปลอดภัยดังนี้
ความปลอดภัย | เหตุการณ์ของ API | รายละเอียด | ความจำเป็นในการ Implementation |
---|---|---|---|
HTTP verbs | Listeners->Path | HTTP verbs ต่าง ๆ สามารถเลือกใช้ได้จาก Path ของ API ที่กำหนดไว้ | ควร |
Input Validation | Filter -> Content Filtering | สามารถ Filters สำหรับการ Validation ข้อมูล Input ตัวอย่างเช่น การตรวจสอบขนาดของข้อความ การตรวจสอบ Schema การตรวจสอบ Headers หรือการตรวจสอบ Query strings เป็นต้น | ควร |
SSL | Listeners | ใช้ SSL protocol (เช่น TLS 1.2) | ควร |
Digital Certificate | Filter -> Integrity | สามารถ Filters สำหรับใช้ Signature เพื่อการตรวจสอบตัวอย่างเช่น API Token หรือ JWT sign | เป็น Optional หรือขึ้นอยู่กับ Business requirements |
JWT | Filter -> Integrity | ข้อความสามารถเข้ารหัสให้อยู่ในรูปแบบ JWT | เป็น Optional หรือขึ้นอยู่กับ Business requirements |
API Keys | Filter -> Authentication | สามารถ Filters สำหรับการ Authentication ผู้ใช้งาน เช่น API Keys หรือ API Token | ควร |
OAuth | Filter -> OAuth | สามารถใช้ OAuth สำหรับมอบสิทธิ์เข้าถึง Data resources สำหรับผู้ใช้งาน | เป็น Optional หรือขึ้นอยู่กับ Business requirements |
CORS | Listeners->Path | CORS สามารถจำกัดการเข้าถึงในระดับ Path level | ควร |
ความปลอดภัยของ API เป็นประเด็นสำคัญ การมีช่องโหว่ในระบบเป็นการเปิดช่องทางให้ผู้โจมตีดำเนินกิจกรรมบางอย่างที่เป็นอันตรายได้