APIs คือ User Interfaces สำหรับนักพัฒนา
API เป็นผลิตภัณฑ์ ผู้ใช้ของมันคือนักพัฒนา และประสบการณ์ของพวกเขาสำคัญเท่าเทียมกับประสบการณ์ผู้ใช้ปลายทาง API ที่ออกแบบดีช่วยลดเวลาการรวม ป้องกันการ bugs และสร้าง support tickets ที่น้อยลง API ที่ออกแบบไม่ดีทำสิ่งตรงข้าม ไม่ว่าเอกสารจะดีแค่ไหนก็ตาม
หลักการด้านล่างมาจากการสร้าง APIs ที่ให้บริการ clients หลายร้อยตัว และใช้งาน APIs ที่ทำให้ฉันอยากโยนแล็ปท็อปของฉัน
URL Design ที่สมเหตุสมผล
ใช้ Nouns ไม่ใช่ Verbs
Resources คือ nouns HTTP method คือ verb GET /users ดึงข้อมูล users POST /users สร้าง user PUT /users/123 อัพเดต user 123 DELETE /users/123 ลบ user 123 คุณไม่ต้อง GET /getUsers หรือ POST /createUser method ได้ถ่ายทำการกระทำแล้ว
ชื่อ Resource ที่เป็น Plural
ใช้ nouns ที่เป็น plural เสมอ: /users, /orders, /products ถึงแม้เมื่อเข้าถึง single resource: GET /users/123 ความไม่สอดคล้องของ /user/123 vs /users สร้างความสับสน และ bikeshed arguments เลือก plural และยึด
Nesting สำหรับ Relationships
ใช้ URL nesting เพื่อแสดง relationships: GET /users/123/orders ส่งกลับ orders สำหรับ user 123 แต่อย่า nest ลึกกว่า 2 ระดับ - /users/123/orders/456/items/789 ยุ่งเหยิง แทน promote deeply nested resources เป็น top-level endpoints: GET /order-items/789
Query Parameters สำหรับ Filtering
Filtering sorting และ pagination อยู่ใน query parameters ไม่ใช่ URL path GET /users?status=active&sort=-created_at&page=2 ชัดเจนและยืดหยุ่น path ระบุ resource query ปรับเปลี่ยนการตอบสนอง
Response Design
Consistent Envelope
ทุกการตอบสนองควรปฏิบัติตามโครงสร้างที่สม่ำเสมอ สำหรับ single resources ส่งกลับ resource โดยตรงพร้อมเมตาดาต้าในส่วนหัว สำหรับ collections รวมเมตาดาต้า pagination พร้อมข้อมูล
ส่วนสำคัญคือ consistency: ทุก endpoint ควรใช้ response shape เดียวกัน นักพัฒนาที่ใช้งาน API ของคุณจะเขียนโค้ด generic handling และ inconsistencies บังคับให้เกิด special cases
ใช้ HTTP Status Codes อย่างถูกต้อง
สิ่งนี้ฟังดูชัดเจน แต่จำนวน APIs ที่ส่งคืน 200 OK พร้อม error body นั้นใหญ่โต ใช้ status codes ทางความหมาย: 200 สำหรับ success 201 สำหรับ creation 204 สำหรับ successful deletion (ไม่มี body) 400 สำหรับ client errors 401 สำหรับ authentication failures 403 สำหรับ authorization failures 404 สำหรับ not found 409 สำหรับ conflicts 422 สำหรับ validation errors และ 500 สำหรับ server errors
ความแตกต่างระหว่าง 401 และ 403 สำคัญ: 401 หมายถึง “ฉันไม่รู้ว่าคุณเป็นใคร” (missing หรือ invalid token) 403 หมายถึง “ฉันรู้ว่าคุณเป็นใคร แต่คุณไม่มีสิทธิ์”
Error Responses
ข้อผิดพลาดควรมีประโยชน์ให้มากที่สุด รวม error code ที่อ่านได้ของเครื่อง (ไม่ใช่แค่ HTTP status) ข้อความที่อ่านได้ของมนุษย์ และรายละเอียด field-level สำหรับ validation errors
อย่าเปิดเผย stack traces หรือรายละเอียด implementation ภายในใน production error responses บันทึกพวกมันบน server-side แต่ส่งกลับ clean error ไปยังไคลเอนต์ database constraint violation ควรกลายเป็น “Email address already in use” ไม่ใช่ “unique constraint violation on users_email_key”
Pagination
Cursor-Based vs Offset-Based
Offset pagination (page=3&limit=20) เป็นสัญชาตญาณ แต่พัง large datasets: ข้ามไป page 1000 ต้องให้ database สแกนและทิ้ง 20,000 rows นอกจากนี้ยังประสบปัญหา drift - ถ้า items ถูกแทรกหรือลบระหว่าง page requests items สามารถ duplicated หรือ skipped
Cursor-based pagination (after=cursor123&limit=20) แก้ปัญหาทั้งสอง cursor โดยปกติคือ encoded version ของ sort key ของ last item อนุญาตให้ database seek ตรงไปยังตำแหน่งที่ถูกต้อง มันเสถียรภายใต้ concurrent modifications และดำเนินการอย่างสอดคล้องโดยไม่คำนึงถึง depth
ใช้ cursor-based pagination สำหรับ collection ใด ๆ ที่อาจเติบโตใหญ่ ใช้ offset pagination เฉพาะสำหรับ small, relatively static datasets ที่ page numbers ใน UI เป็น hard requirement
Pagination Response
รวมเมตาดาต้าเพียงพอสำหรับไคลเอนต์เพื่อนำทาง: items ของ current page flag บ่งชี้ว่า more items มีอยู่ และ cursor สำหรับ next page สำหรับ offset pagination รวม total count และ total pages อย่าทำให้ไคลเอนต์ guess ว่าพวกเขาถึง end หรือไม่
Versioning
URL-Based Versioning
ฉันใช้ header-based query parameter-based และ URL-based versioning URL versioning (/v1/users, /v2/users) ชนะด้วยเหตุผลปฏิบัติ: มันมองเห็นได้ในบันทึก ตัวกำหนดเส้นทาง easy ใน infrastructure level และ obvious สำหรับนักพัฒนา ข้อโต้แย้งทางทฤษฎีสำหรับ content negotiation headers เป็น elegant แต่impractical - นักพัฒนาดำเนินการ forget ตั้ง headers เป็นประจำ และ debugging version issues กลายเป็น harder
เมื่อ Version
Version เมื่อคุณสร้าง breaking changes: ลบ fields เปลี่ยน field types เปลี่ยน response structures หรือเปลี่ยน authentication อย่า version สำหรับ additive changes - new fields และ new endpoints เป็น backward-compatible และไม่ต้อง version bump
maintain ที่มากที่สุด 2 versions พร้อมกัน ซัพพอร์ต v1 v2 และ v3 เป็น operational burden ที่เติบโต quadratically กำหนดชัดเจน deprecation timelines และสื่อสาร aggressively
Rate Limiting
Rate limiting ป้องกัน API ของคุณจากการละเมิดและ ตรวจสอบการใช้งานที่ยุติธรรม ส่งกลับข้อมูล rate limit ใน response headers: limit remaining requests และ reset time เมื่อไคลเอนต์เกิน limit ส่งกลับ 429 Too Many Requests พร้อม Retry-After header
ใช้ rate limiting ต่อ API key หรือต่อ authenticated user ไม่ใช่ต่อ IP address IP-based limiting พัง สำหรับไคลเอนต์ที่อยู่ behind NAT หรือ corporate proxies ที่ thousands of users share IP
Authentication
ใช้ bearer tokens (JWT หรือ opaque tokens) ใน Authorization header อย่าใส่ tokens ใน URL parameters - พวกมันลงท้ายใน server logs browser history และ referrer headers
สำหรับ server-to-server communication API keys โอเค สำหรับ user-facing applications OAuth 2.0 พร้อม short-lived access tokens และ refresh tokens คือมาตรฐาน อย่าประดิษฐ์ authentication - security implications ของการไม่ถูก มหาศาล
Documentation
API ของคุณต้อง 3 ประเภท documentation: reference (ทุก endpoint ทุก parameter ทุก response) getting-started guide (authenticate และ ทำ first request ของคุณในเวลา under 5 minutes) และ runnable examples (curl commands หรือ SDK snippets ที่ work เมื่อ copy-pasted)
OpenAPI/Swagger specifications สร้าง reference documentation โดยอัตโนมัติและเก็บมัน sync กับ implementation ของคุณ แต่ auto-generated docs เป็น starting point ไม่ใช่ finish line - คุณยังคง ต้องการ prose explanations examples และ tutorials
The Test ของ Good API Design
นักพัฒนาควรสามารถ guess behavior ของ API ของคุณจากการดู 1 endpoint ถ้าพวกเขารู้ว่า GET /users ส่งกลับ list of users พร้อม pagination พวกเขาควรสามารถ predict ว่า GET /orders ทำสิ่งเดียวกัน ว่า POST /orders สร้าง order และว่า GET /orders/123 ส่งกลับ specific order ความสอดคล้องคือ virtue สูงสุดของ API design