Các phần mềm mà chúng ta xây dựng luôn luôn phát triển, thay đổi và cải tiến liên tục. Điều này dẫn đến một vấn đề: làm thế nào để chúng ta thực hiện những cải tiến này mà không gây ảnh hưởng nghiêm trọng cho người dùng? Ví dụ như crash, đơ, phản hồi chậm hoặc timeout. Trong bài viết này, chúng ta sẽ khám phá cơ chế phổ biến nhất để giải quyết vấn đề phiên bản hoá (versioning) cho API nhé.
1. Tại sao phải chia phiên bản API?
Giả sử bạn đang vận hành một ứng dụng giao nhận hàng hoá (Giaohangnhanh, Ahamove) và bạn cung cấp một API cho các nhà phát triển bên thứ ba để họ có thể tích hợp dịch vụ giao nhận của bạn vào các ứng dụng hoặc hệ thống của họ (Shopee, Tiki).
Phiên bản ban đầu của API: Bạn có một API với endpoint /api/orders
để tạo đơn hàng mới. API này yêu cầu các thông tin như tên người dùng, địa chỉ giao hàng, và danh sách món ăn.
POST /api/orders
{
"username": "Nguyen Van A",
"address": "Ho Chi Minh",
"items": [
{
"item_id": "123",
"quantity": 2
}
]
}
Trực tiếp thay đổi trên API ban đầu: Sau một thời gian, bạn quyết định cập nhật API để hỗ trợ thêm tính năng mới: cho phép người dùng chọn thời gian giao hàng mong muốn. Bạn cập nhật endpoint /api/orders
để yêu cầu thêm trường delivery_time
.
POST /api/orders
{
"username": "Nguyen Van A",
"address": "Ho Chi Minh",
"items": [
{
"item_id": "123",
"quantity": 2
}
],
"delivery_time": "2024-08-01T12:30:00"
}
Hậu quả: Các ứng dụng đã tích hợp API của bạn từ trước đó sẽ không biết về sự thay đổi này. Khi họ gửi yêu cầu mà không có delivery_time
, máy chủ của bạn sẽ trả về lỗi, vì giờ đây delivery_time
là một trường bắt buộc.
Bạn có thể tạo một endpoint mới như /api/v2/orders
để hỗ trợ tính năng delivery_time
. Điều này đảm bảo rằng các ứng dụng hiện tại vẫn có thể tiếp tục sử dụng phiên bản cũ mà không bị ảnh hưởng, trong khi các ứng dụng mới có thể sử dụng phiên bản mới với tính năng bổ sung.
2. Nguyên tắc khi tạo Phiên bản API
Dưới đây là một số nguyên tắc chính cần nắm rõ khi thực hiện API Versioning:
- Phiên bản API không nên làm gián đoạn hoạt động hiện tại của client: Cần phải đảm bảo rằng việc này sẽ không làm hỏng ứng dụng của khách hàng hiện tại, nếu không điều này sẽ dễ dàng gây khó chịu cho cộng đồng phát triển.
- Giữ tần suất phát hành các phiên bản API ở mức tối thiểu: Mỗi khi một phiên bản API mới được phát hành, các Developer cần phải đọc tài liệu, phân tích tác động của thay đổi lên các ứng dụng của họ, gỡ lỗi, v.v. Điều này là một gánh nặng lớn đối với cả hai bên về mặt thời gian và tiền bạc. Facebook thường phát hành version Graph API mới khoảng 1 năm 1 lần.
- Các thay đổi nên tương thích với phiên bản trước và tránh tạo ra các phiên bản API mới: Cách đơn giản nhất để tránh tạo ra các phiên bản API mới là làm đảm bảo những thay đổi của bạn tương thích với các phiên bản trước đây, VD: thêm tham số đầu vào mới nhưng để là không bắt buộc (optional), ví dụ như cột
delivery_time
ở ví dụ bên trên, nếu client truyền hay không truyên thêm tham số này thì chúng ta vẫn xử lý được. - Phiên bản của API không nên tương ứng với phiên bản phần mềm: Tốc độ sinh ra các version của phần mềm nhanh hơn rất nhiều so với version của API. Vì thế nếu không có cập nhật lớn nào thì cứ giữ nguyên version của API.
3. Cách đặt tên Phiên bản cho API
Semantic Versioning là một phương pháp đặt tên version phổ biến nhất hiện nay, nhất là các phiên bản SDK, Application và cả API nữa. Semantic Versioning có cấu trúc là MAJOR.MINOR.PATCH
(x.x.x), API version chỉ sử dụng một phần trong phương pháp của Semantic chứ không áp dụng 100%, mình sẽ giải thích rõ trong phần 4 bên dưới.
- MAJOR: Số đầu tiên trong chuỗi đại diện cho phiên bản chính. Chúng ta tăng số này lên khi có những thay đổi lớn hoặc phiên bản mới không tương thích với các phiên bản trước.
- MINOR: Số thứ hai trong chuỗi phiên bản đại diện cho phiên bản phụ. Nó thay đổi khi bạn thêm tính năng mới vào API nhưng vẫn duy trì sự tương thích với các phiên bản trước đó.
- PATCH: Số thứ ba trong chuỗi phiên bản đại diện cho bản vá. Nó thay đổi khi bạn sửa lỗi hoặc thực hiện các thay đổi nhỏ mà không thêm tính năng mới hoặc phá vỡ sự tương thích.
4. Các phương pháp tạo Phiên bản API
4.1 Sử dụng URL
Version 1: http://www.example.com/v1/customers
Version 2: http://www.example.com/v2/customers
Trong URL này, v1,v2
là tượng trưng cho việc thay đổi version khi có một cập nhật lớn (major) xảy ra. Bạn sẽ thắc mắc lại sao mình không tuân theo phương pháp Semantic đã đề cập đến ở bên trên ? Giải thích ở ngay bên dưới nhé
Ở hình trên bạn sẽ thấy Facebook không tuân thủ phương pháp đặt tên của API 100% như Semantic Versioning, ngay cả Shopee và Google cũng thế, vì version API rất quan trong với các đối tác mà họ làm việc cùng hay cả chính Client của họ (Shopee App, Web), điều này phản ánh ở thời gian tăng version (MAJOR), họ tăng mỗi năm một lần. Nếu mỗi tháng Facebook ra một bản cập nhật API mới chắc Developer sẽ khóc thét, nên thay vào đó họ sẽ cố gắng làm cho version hiện tại tương thích với các phiên bản trước đó nhiều nhất có thể.
4.2 Sử dụng HTTP Header
Một cách tiếp cận khác là sử dụng HTTP header, với phương pháp này, client sử dụng HTTP header để chỉ định phiên bản API mà họ muốn gọi. Lợi thế của cách tiếp cận này là giúp giữ cho phiên bản API không xuất hiện trong URL.
GET /api/customers
Host: api.example.com
Accept: application/vnd.example.v1+json
Lợi ích khác của phương pháp này là bạn có thể dễ dàng bỏ qua hoặc nâng cấp một cách âm thầm nếu người dùng không chỉ định phiên bản API nào trong header. Đây là phương pháp nâng version mà Github đang thực hiện, để version trong phần Accept: Content-Type
, hoặc chúng ta có thể dùng biến X-API-version
4.3 Sử dụng Query Parameters
Client sẽ chỉ định phiên bản dưới dạng một tham số truy vấn trong Request
http://www.example.com/customers?version=v2.0.0
Server có thể lựa chọn chấp nhận tham số truy vấn này hoặc thậm chí bỏ qua nó, nếu client không truyền version chúng ta có thể xem như họ muốn dùng phiên bản mới nhất, điều mà phương pháp URL không làm được.
4.4 Sử dụng Hostname
Bạn cũng có thể tạo phiên bản API sử dụng một hostname khác, ví dụ: phiên bản API đầu tiên của Facebook cung cấp ở địa chỉ api.facebook.com
, trong khi API mới của lại nằm địa chỉ hoàn toàn khác graph.facebook.com
. Cách tiếp cận này chỉ được sử dụng khi có một sự cải tổ toàn diện về API.
5. Kết luận
Việc lựa chọn phương pháp versioning phù hợp không chỉ dựa trên các yếu tố kỹ thuật mà còn phải cân nhắc đến nhu cầu của người dùng API, khả năng quản lý hệ thống, và yêu cầu về tương thích. Dù chọn phương pháp nào, mục tiêu cuối cùng là đảm bảo API của bạn có thể phát triển theo thời gian mà không gây gián đoạn cho người dùng hiện tại, đồng thời tạo điều kiện thuận lợi cho việc mở rộng và cải tiến hệ thống trong tương lai.
Các bài viết liên quan:
Bài viết liên quan
Giới thiệu Kiến trúc Backend for Frontend (BFF)
Nov 16, 2024 • 10 min read
Flask là gì? Hướng dẫn tạo Ứng dụng Web với Flask
Nov 15, 2024 • 7 min read
Webhook là gì? So sánh Webhook và API
Nov 15, 2024 • 8 min read
Spring Boot là gì? Hướng dẫn Khởi tạo Project Spring Boot với Docker
Nov 14, 2024 • 6 min read
Two-Factor Authentication (2FA) là gì? Vì sao chỉ Mật khẩu thôi là chưa đủ?
Nov 13, 2024 • 7 min read
Test-Driven Development (TDD) là gì? Hướng dẫn thực hành TDD
Nov 13, 2024 • 6 min read