

Laravel API Versioning - ดีไซน์ API ให้รองรับอนาคต
การวางแผนการจัดการ API ไม่ใช่เรื่องไกลตัว แต่เป็นเรื่องที่ต้องวางแผนจัดการตั้งแต่แรก เพื่อรองรับการเปลี่ยนแปลงในระยะยาว
หากคุณเป็น developer ที่เขียน Laravel อยู่แล้ว การจะสร้าง API ขึ้นมาใช้งานคงไม่ใช่เรื่องยากและถ้าผู้ใช้งานเป็น developer ในทีมเดียวกันด้วยแล้วก็คงจะไม่เกิดปัญหาอะไร แต่ถ้า API ที่เราเขียน จำเป็นต้องให้บริการแก่ client หลายคนหรือหลายฝ่าย เช่น web frontend หรือ mobile application อาจมีโอกาสที่ client ทั้ง 2 นี้ต้องการข้อมูลที่แตกต่างกัน
หากเราอัปเดต Version ของ API ผิดพลาด ผลก็คืออาจทำให้ client ที่ใช้งานอยู่บางส่วนพังได้ทันทีโดยไม่รู้ตัว และคงจะเป็นเรื่องยากยิ่งกว่าเดิมแน่ ๆ หากระบบเรามีความซับซ้อนมากยิ่งขึ้น โอกาสที่การแก้ไขของเราจะทำให้ client พังก็ยิ่งมีมากขึ้นไปอีก นั่นทำให้การวางแผนเรื่อง API Versioning จึงเป็นเรื่องที่สำคัญและไม่ควรมองข้าม
💡 API Versioning คืออะไร#
API Versioning คือ การกำหนดรุ่น หรือ Version ให้กับ API แต่ละเส้นของเรา เพื่อให้ผู้ใช้งานสามารถแยกการใช้งานและกำหนดคุณลักษณะของ client ที่เรียกใช้แยกออกจากกัน เช่น web frontend หรือ mobile application เป็นต้น เพื่อให้ client แต่ละตัวสามารถทำงานได้ต่อเนื่องแม้ว่าทาง backend จะมีการอัปเดตเวอร์ชันนั่นเอง
📑 รูปแบบของ API Versioning#
การกำหนดเวอร์ชันให้กับ API สามารถทำได้หลายรูปแบบ เช่น
- URI Versioning - เป็นการกำหนดเวอร์ชันของ API ผ่าน path ของ endpoint เช่น
/api/v1/users และ /api/v2/users
- Header Versioning - เป็นการกำหนดเวอร์ชันของ API ผ่าน request header เช่น
X-API-Version: 2 และ Accept: application/vnd.example.v2+json
- Query Parameter - เป็นการกำหนดเวอร์ชันของ API ผ่าน query parameters ของ endpoint เช่น
/api/users?version=1 และ /api/users?version=2
จะเห็นว่าแต่ละวิธีที่เลือกใช้ก็จะมีข้อดีและข้อเสียแตกต่างกัน ขึ้นอยู่กับว่าผู้พัฒนาต้องการผลลัพธ์แบบใด
🛠️ การออกแบบ API Versioning บน Laravel#
สำหรับในบทความนี้ แอดมินจะขอแนะนำวิธีการบริหารจัดการ API Versioning ในรูปแบบของ URI Versioning ซึ่งเป็นรูปแบบที่ผู้อ่านน่าจะพบเห็นได้ทั่วไป และตัว Laravel เองก็มีฟีเจอร์สำหรับแยก prefix และ group ของ API อยู่แล้วจึงทำให้สามารถบริหารจัดการได้ง่าย
โดยหัวข้อที่เกี่ยวกับการพัฒนาแบ่งออกเป็นส่วน ๆ ดังนี้
การใช้ Route Group และ Namespace#
ในขั้นแรกของการจัดการ API Versioning เราต้องทำการแยก route ของ API แต่ละเวอร์ชันออกจากกันเสียก่อน และให้ API แต่ละเวอร์ชันเรียกผ่าน Controller ที่ Namespace ตรงกับเวอร์ชันของ API ตัวอย่างเช่น
Route::prefix('v1')->group(function () {
Route::get('/users', [App\Http\Controllers\V1\UserController::class, 'index']);
});
Route::prefix('v2')->group(function () {
Route::get('/users', [App\Http\Controllers\V2\UserController::class, 'index']);
});phpจากตัวอย่างจะเห็นว่า เรามี endpoint 2 เส้นคือเส้น v1 และ v2 ซึ่งถูกบริหารจัดการด้วย Controller ที่แตกต่างกัน
การใช้ Service Layer เพื่อลดความซ้ำซ้อนของโค้ดใน Controller#
หากเราแยก Controller ออกเป็น 2 เวอร์ชันแล้ว อาจมีโค้ดบางส่วนที่เหมือนเดิมซึ่งทำให้ยากต่อการบำรุงรักษาในอนาคต ในจุดนี้เราจะใช้ Service Layer เข้ามาช่วยลดความซ้ำซ้อนของโค้ดบางส่วนลง เช่น
class UserService {
public function getUsers() {
return User::all();
}
}
// Controller V1
class UserControllerV1 extends Controller {
public function index(UserService $service) {
// Business logic for V1
return response()->json($service->getUsers());
}
}
// Controller V2
class UserControllerV2 extends Controller {
public function index(UserService $service) {
// Business logic for V2
return response()->json($service->getUsers());
}
}phpจากตัวอย่างจะเห็นว่า แม้เราจะแยก Controller ออกเป็น 2 ไฟล์แล้วก็ตาม แต่การใช้ Service Layer เข้ามาช่วยจะทำให้โค้ดภายใน Controller ทั้ง 2 ไฟล์นี้ แทบจะไม่มีความซ้ำซ้อนกันอยู่เลย สิ่งที่แตกต่างกันมีเพียงโครงสร้างที่สำคัญเท่านั้นเอง
✔️ Best Practices และแนวทางที่ควรทำ#
แม้ว่าการแยก API Versioning ออกเป็นหลายเวอร์ชัน จะเป็นสิ่งที่ดีและควรทำ แต่หากไม่บริหารจัดการให้ดีก็อาจทำให้เกิดปัญหาที่คาดไม่ถึงขึ้นมาในภายหลังได้ สำหรับ Best Practices ที่ดีของการจัดการ API Versioning ที่แอดมินแนะนำว่าควรทำควบคู่กันไปมีดังนี้
- เปลี่ยนเวอร์ชัน เมื่อมี breaking changes เท่านั้น
แม้ว่าการเปลี่ยนเวอร์ชันใน Laravel จะสามารถทำได้โดยง่าย แต่หากเราต้องเปลี่ยนเวอร์ชันทุกครั้งที่มีการแก้ไขเล็ก ๆ น้อย ๆ ก็อาจทำให้โค้ดของเราบวมขึ้นมาได้ และจะกลายเป็นว่าเวอร์ชันที่เพิ่มขึ้นมานั้นทำให้ยากต่อการบำรุงรักษาเสียเอง
ซึ่งในจุดนี้แอดมินแนะนำว่าให้เปลี่ยนเวอร์ชันเมื่อโครงสร้างของ API เปลี่ยน เช่น request parameters หรือ JSON response เท่านั้นก็เพียงพอ (หรือการ breaking changes อื่น ๆ ตามที่ทีมตกลงร่วมกัน)
- เขียนเอกสารกำกับให้ชัดเจน
กรณีที่ API มีหลายเส้นและหลายเวอร์ชัน อาจทำให้ client หรือผู้ใช้งานเกิดความสับสนถึงโครงสร้างของ request และ response ที่อาจเปลี่ยนแปลงไปได้ เราจึงควรมีเอกสารกลางไว้กำกับคุณสมบัติของ API แต่ละเวอร์ชันเพื่อให้การสื่อสารระหว่างผู้พัฒนา API และผู้ใช้งานมีความชัดเจนมากยิ่งขึ้น
- วางแผน Deprecation
บางครั้ง API เวอร์ชันเก่าที่โครงสร้างไม่รองรับแล้วและจำเป็นต้องหยุดให้บริการลงด้วยเหตุผลต่าง ๆ เช่น ความปลอดภัยหรือ Performance ของ software ในกรณีนี้ผู้พัฒนาควรวางแผน Deprecation และแจ้งผู้ใช้งานก่อนที่จะหยุดให้บริการ เพื่อให้ผู้ใช้งานสามารถปรับปรุง website หรือ application ให้รองรับกับ API เวอร์ชันปัจจุบันได้
- วางแผน Test API แต่ละเวอร์ชันแยกกัน
แม้การทำงานของ API แต่ละเวอร์ชันจะคล้ายกัน แต่เมื่อมีการพัฒนาก็ย่อมมีความแตกต่างกันอย่างแน่นอน (ยิ่งรวมกับว่าเราแยก API เมื่อ breaking changes) ทำให้การทดสอบ API แต่ละเวอร์ชันควรทำแยกกัน เพื่อให้มั่นใจได้ว่า API แต่ละเวอร์ชันยังคงสามารถทำงานได้อย่างถูกต้องแม้จะมีการเพิ่มเติมหรือแก้ไขใด ๆ
👩💻 บทสรุป#
แม้ว่าเรื่อง API Versioning จะดูเป็นเรื่องเล็กน้อยแต่ก็เป็นสิ่งสำคัญที่จะช่วยให้ระบบสามารถพัฒนาและเปลี่ยนแปลงได้โดยไม่กระทบผู้ใช้เดิม และเมื่อวางแผนอย่างเป็นระบบตั้งแต่ต้น ก็จะช่วยลดปัญหาในอนาคต และสร้างความมั่นใจให้ทั้งทีมพัฒนาและผู้ใช้งาน API อีกด้วย