Mở đầu — vì sao bài này quan trọng
Ở Bài 1, bạn đã nghe đến cụm từ "REST API" như một thứ gì đó hiển nhiên: cứ gọi HTTP, trả về JSON là REST. Nhưng đó mới chỉ là cái vỏ bề ngoài. Trên thực tế, rất nhiều API mà các đội phát triển gọi là "RESTful" lại vi phạm gần như toàn bộ những gì làm nên REST đúng nghĩa. Hậu quả là hệ thống khó mở rộng, khó cache, dễ vỡ khi tích hợp, và mỗi lần thêm tính năng là một lần "đập đi xây lại".
Là một Business Analyst (BA), bạn không phải người viết code dựng API, nhưng bạn là người đọc spec, review thiết kế, và đặt câu hỏi đúng trong các buổi họp với đội kỹ thuật và đối tác tích hợp. Khi một đối tác đưa cho bạn tài liệu API và nói "đây là REST chuẩn", bạn cần đủ hiểu biết để kiểm chứng điều đó, chứ không gật đầu cho qua. Khi đội dev tranh luận về một thiết kế, bạn cần biết REST thực ra ràng buộc những gì để góp tiếng nói có trọng lượng.
Bài này đi sâu vào gốc rễ lý thuyết của REST: 6 nguyên tắc (constraints) do Roy Fielding đề xuất trong luận án tiến sĩ năm 2000. Hiểu được 6 ràng buộc này, bạn sẽ phân biệt được "API trông giống REST" với "API thật sự RESTful", và quan trọng hơn, bạn hiểu tại sao những ràng buộc đó tồn tại và mang lại lợi ích kinh doanh gì.
Khái niệm cốt lõi
REST không phải là một protocol
Đây là hiểu lầm phổ biến nhất. REST (REpresentational State Transfer) không phải là một giao thức như HTTP, không phải một chuẩn kỹ thuật bạn cài đặt, cũng không phải một thư viện. REST là một kiến trúc (architectural style) — một tập hợp các ràng buộc (constraints) mà nếu hệ thống tuân theo, nó sẽ có những đặc tính tốt: dễ mở rộng, dễ bảo trì, độ tin cậy cao, hiệu năng tốt.
Roy Fielding — một trong những người đồng tác giả chuẩn HTTP — đã đưa ra REST trong luận án tiến sĩ "Architectural Styles and the Design of Network-based Software Architectures" (Đại học California, Irvine, năm 2000). Ông không phát minh ra HTTP rồi gọi nó là REST; ngược lại, ông phân tích tại sao Web (vốn dựa trên HTTP) lại mở rộng được tới quy mô toàn cầu, rồi đúc kết các nguyên tắc đó thành REST.
Điểm mấu chốt: REST mô tả các ràng buộc. Mỗi ràng buộc bạn thêm vào sẽ giới hạn một số tự do trong thiết kế, nhưng đổi lại mang một lợi ích cụ thể. Một API tuân thủ càng nhiều ràng buộc thì càng "RESTful". Đây là tinh thần cần nắm trước khi đi vào từng nguyên tắc.
Nguyên tắc 1 — Client–Server (tách biệt client và server)
Ràng buộc đầu tiên: tách biệt rõ ràng giữa client (bên gọi, ví dụ ứng dụng mobile, trình duyệt) và server (bên cung cấp dữ liệu và xử lý nghiệp vụ). Hai bên chỉ giao tiếp qua một giao diện thống nhất (interface), không bên nào cần biết chi tiết nội bộ của bên kia.
Lợi ích kinh doanh: đội làm app mobile và đội làm backend có thể phát triển song song và độc lập. Backend có thể đổi từ database này sang database khác mà app không cần sửa, miễn là interface không đổi. Đây chính là lý do một API duy nhất có thể phục vụ cùng lúc web, iOS, Android và đối tác bên thứ ba.
Nguyên tắc 2 — Stateless (không lưu trạng thái phiên)
Mỗi request từ client phải chứa đầy đủ thông tin để server hiểu và xử lý nó. Server không lưu trạng thái phiên (session state) giữa các request. Nếu cần thông tin xác thực, mỗi request phải tự mang theo (ví dụ token trong header), chứ không dựa vào việc "server nhớ tôi đã đăng nhập ở request trước".
Lợi ích: server dễ mở rộng theo chiều ngang. Bạn có thể đặt 10 server giống hệt nhau sau một load balancer, và request thứ nhất đi vào server A, request thứ hai đi vào server B cũng không sao, vì server nào cũng xử lý được độc lập. Đây là nền tảng để hệ thống chịu tải lớn.
Nguyên tắc 3 — Cacheable (có thể cache)
Mỗi response phải tự đánh dấu rõ nó có thể được cache (lưu tạm) hay không, và cache trong bao lâu. Khi response đánh dấu là cacheable, client hoặc các tầng trung gian có thể tái sử dụng nó cho các request giống nhau, không cần gọi lại server.
Lợi ích: giảm số lần gọi server, giảm độ trễ, tiết kiệm băng thông và chi phí hạ tầng. Một danh sách sản phẩm ít thay đổi có thể cache 5 phút, giảm hàng triệu lượt truy vấn database mỗi ngày.
Nguyên tắc 4 — Uniform Interface (giao diện thống nhất)
Đây là ràng buộc trung tâm và quan trọng nhất của REST, khiến nó khác biệt với các kiến trúc khác. Uniform Interface gồm 4 nguyên tắc con:
- Identification of resources (định danh tài nguyên): mỗi tài nguyên (resource) được định danh bằng một URI duy nhất. Ví dụ
/orders/12345đại diện cho đơn hàng số 12345. Tài nguyên là danh từ, không phải hành động. - Manipulation through representations (thao tác qua biểu diễn): client không thao tác trực tiếp lên tài nguyên trên server, mà thông qua một biểu diễn (representation) của nó — thường là JSON hoặc XML. Client nhận biểu diễn, sửa, gửi lại để cập nhật.
- Self-descriptive messages (thông điệp tự mô tả): mỗi message chứa đủ thông tin để bên nhận hiểu cách xử lý — ví dụ header
Content-Type: application/jsoncho biết body là JSON. - HATEOAS (Hypermedia as the Engine of Application State): response không chỉ chứa dữ liệu mà còn chứa các liên kết chỉ ra hành động tiếp theo client có thể làm. Đây là phần thường bị bỏ qua nhất trong thực tế, và cũng là phần phân biệt REST "đúng chuẩn" với REST "trên giấy tờ".
/customers/99/orders là hiểu ngay đó là các đơn hàng của khách số 99, không cần đọc tài liệu dày.Nguyên tắc 5 — Layered System (hệ thống phân tầng)
Kiến trúc được chia thành nhiều tầng (layer), và mỗi tầng chỉ biết tầng ngay kề nó. Client gọi API mà không biết phía sau là một server đơn lẻ hay cả một dàn server, có load balancer, có tầng cache, có API Gateway hay không. Các tầng trung gian (proxy, gateway, cache) có thể được thêm vào mà client không hề hay biết.
Lợi ích: bạn có thể chèn các tầng bảo mật, cân bằng tải, giám sát vào giữa mà không phá vỡ client. Hệ thống tiến hóa được mà không bắt mọi bên cùng thay đổi một lúc.
Nguyên tắc 6 — Code on Demand (tùy chọn)
Đây là ràng buộc duy nhất không bắt buộc. Server có thể gửi mã thực thi (ví dụ JavaScript) xuống client để client chạy, mở rộng tính năng động. Vì là tùy chọn, đa số API nghiệp vụ không dùng đến, nên bạn chỉ cần biết nó tồn tại.
Tình huống thực tế
Ví dụ 1 — Ví điện tử ở Việt Nam vi phạm Stateless
Một ví điện tử giả định, gọi là PayViet, thiết kế API thanh toán theo kiểu: client gọi POST /login để đăng nhập, server tạo một session ID lưu trong bộ nhớ của chính server đó, rồi các request sau như POST /pay dựa vào session đang "sống" trên server để biết người dùng là ai.
Khi lượng giao dịch tăng vọt dịp Tết, đội vận hành thêm server thứ hai sau load balancer. Lập tức lỗi xảy ra hàng loạt: người dùng login trên server A, nhưng request thanh toán bị load balancer đẩy sang server B — nơi không hề có session của họ — nên bị từ chối "phiên hết hạn". Đội phải vá tạm bằng "sticky session" (ép mỗi người dùng dính chặt vào một server), nhưng cách này khiến tải phân bổ không đều và khi một server chết thì toàn bộ người dùng trên đó bị văng.
Bài học: vi phạm ràng buộc Stateless khiến hệ thống không mở rộng ngang được. Nếu mỗi request tự mang token xác thực (ví dụ một Bearer token), server nào cũng xử lý được, và việc thêm server chỉ là chuyện chèn thêm máy. Là BA review thiết kế, khi thấy spec ghi "server lưu session trong RAM", bạn phải đặt câu hỏi ngay về khả năng mở rộng.
Ví dụ 2 — Sàn thương mại điện tử và sức mạnh của Uniform Interface
Một sàn TMĐT khu vực Đông Nam Á (bối cảnh tương tự Shopee, Lazada) mở API cho hàng nghìn nhà bán hàng tự tích hợp hệ thống kho của họ. Họ thiết kế chặt theo Uniform Interface: tài nguyên định danh rõ ràng như /shops/{shopId}/products/{productId}, /shops/{shopId}/orders, mọi response đều có Content-Type chuẩn, và quan trọng là response đơn hàng kèm các liên kết HATEOAS chỉ ra hành động tiếp theo — ví dụ một đơn ở trạng thái "chờ xác nhận" sẽ kèm link confirm và cancel; còn đơn đã giao thì hai link đó biến mất.
Kết quả: một nhà bán hàng vừa tích hợp xong phần sản phẩm, khi chuyển sang làm phần đơn hàng gần như không cần đọc thêm tài liệu vì cấu trúc URI và cách thao tác hoàn toàn nhất quán. Thời gian tích hợp trung bình của đối tác giảm từ khoảng 3 tuần xuống còn 5–7 ngày. Bộ phận hỗ trợ kỹ thuật cũng nhận ít ticket hơn hẳn vì các link HATEOAS dẫn dắt đối tác làm đúng hành động hợp lệ ở từng trạng thái.
Bài học: Uniform Interface không phải lý thuyết suông — nó trực tiếp rút ngắn thời gian onboarding đối tác và giảm chi phí hỗ trợ. Là BA, khi viết yêu cầu cho một public API, hãy đề xuất quy ước đặt tên tài nguyên nhất quán và cân nhắc đưa link hành động vào response.
Ví dụ 3 — Layered System cứu một đợt tăng tải ngân hàng
Một ngân hàng giả định, VietBank, có API tra cứu số dư mà ban đầu client (app mobile) gọi thẳng vào server ứng dụng. Vào ngày trả lương cuối tháng, lượng tra cứu tăng gấp 8 lần, server quá tải, app chậm và treo.
Vì hệ thống được thiết kế theo Layered System, đội hạ tầng chèn được một tầng API Gateway và một tầng cache vào giữa app và server ứng dụng — mà không cần cập nhật app trên điện thoại hàng triệu người dùng. Gateway gánh phần giới hạn tốc độ và phân tải; tầng cache phục vụ các truy vấn số dư lặp lại trong vài giây. App hoàn toàn không biết có sự thay đổi này, vì với nó, "địa chỉ API" vẫn y nguyên.
Bài học: vì client chỉ biết tầng kề nó, đội kỹ thuật có thể tiến hóa hạ tầng phía sau một cách linh hoạt. Nếu thiết kế ban đầu để client "biết quá nhiều" về cấu trúc backend, một thay đổi như vậy sẽ buộc phải phát hành lại app — điều gần như bất khả thi trong tình huống khẩn cấp.
Hướng dẫn từng bước
Khi bạn cầm trong tay một tài liệu API và cần đánh giá nó "RESTful" tới đâu, hãy đi qua quy trình sau:
- Kiểm tra Client–Server: Xác nhận client và server giao tiếp thuần qua API, không có giả định ngầm rằng client biết chi tiết nội bộ server (ví dụ tên bảng database lộ ra trong response).
- Kiểm tra Stateless: Đọc xem mỗi request có tự mang đủ thông tin xác thực không. Tìm dấu hiệu xấu như "server lưu session", "phụ thuộc request trước". Nếu có, đặt câu hỏi về khả năng mở rộng.
- Kiểm tra Cacheable: Xem các response có header điều khiển cache không (ví dụ
Cache-Control). Các tài nguyên đọc nhiều, đổi ít có được đánh dấu cache không? - Kiểm tra Uniform Interface: Đây là phần soi kỹ nhất. Tài nguyên có được đặt tên bằng danh từ không (
/orderschứ không phải/getOrders)? URI có nhất quán không? Response cóContent-Typerõ ràng không? Có link hành động (HATEOAS) không? - Kiểm tra Layered System: Hỏi đội kỹ thuật xem giữa client và server có hay sẽ có các tầng trung gian (gateway, cache, load balancer) không, và client có bị ràng buộc cứng vào cấu trúc đó không.
- Code on Demand: Thường bỏ qua. Chỉ ghi nhận nếu API có trả về mã để client thực thi.
- Tổng kết & đối thoại: Lập một bảng ngắn: ràng buộc nào đã tuân thủ, ràng buộc nào vi phạm, vi phạm gây rủi ro gì. Dùng bảng này làm cơ sở trao đổi với đội dev và đối tác.
Lỗi thường gặp & mẹo
- Nhầm "trả JSON qua HTTP" là RESTful: Rất nhiều API đặt endpoint kiểu
/getUserData?id=5,/doPayment— đây là phong cách RPC dùng động từ trong URL, vi phạm Uniform Interface. RESTful đúng là/users/5với phương thức HTTP (GET, POST...) thể hiện hành động. - Bỏ quên Stateless rồi đổ lỗi cho hạ tầng: Khi hệ thống không mở rộng được, nhiều đội đổ cho "server yếu" trong khi gốc rễ là thiết kế lưu state. Hãy soi ràng buộc Stateless trước.
- Coi HATEOAS là viển vông: Đúng là ít API thương mại làm HATEOAS đầy đủ, nhưng ngay cả mức tối thiểu (đính kèm vài link hành động hợp lệ) cũng giảm đáng kể lỗi tích hợp. Đừng gạt bỏ nó hoàn toàn.
- Mẹo dùng tủ thuật ngữ: Khi họp với đối tác, gọi đúng tên ràng buộc bị vi phạm ("thiết kế này vi phạm Stateless") khiến góp ý của bạn thuyết phục hơn nhiều so với "tôi thấy hơi kỳ".
- Mẹo ưu tiên: Trong 6 ràng buộc, với BA hãy ưu tiên nắm chắc Stateless và Uniform Interface — đây là hai thứ ảnh hưởng trực tiếp và nhiều nhất tới khả năng mở rộng và chi phí tích hợp.
Bài tập thực hành
- Phân loại endpoint: Cho danh sách sau, đánh dấu cái nào RESTful, cái nào không, và sửa lại cho đúng:
GET /getAllOrders,POST /orders,GET /users/42,POST /deleteCustomer?id=7,GET /products/99/reviews. - Soi spec Stateless: Tìm một tài liệu API công khai bất kỳ (ví dụ của một cổng thanh toán VN), đọc phần xác thực và trả lời: mỗi request tự mang token hay dựa vào session server? Ghi lại lập luận của bạn.
- Thiết kế URI: Bạn được giao thiết kế API cho một hệ thống quản lý khóa học. Hãy đặt tên URI (theo Uniform Interface) cho các tài nguyên: danh sách khóa học, một khóa học cụ thể, danh sách bài học của một khóa, ghi danh học viên vào khóa.
- Bảng đánh giá: Chọn một API bạn từng tiếp xúc trong công việc, lập bảng 6 ràng buộc và đánh dấu mức tuân thủ từng cái. Viết một đoạn ngắn nêu rủi ro lớn nhất từ ràng buộc bị vi phạm nặng nhất.
Tóm tắt
REST không phải là protocol hay thư viện, mà là một kiến trúc do Roy Fielding đề xuất năm 2000, gồm các ràng buộc mà hệ thống tuân theo càng nhiều thì càng "RESTful". Sáu nguyên tắc gồm: Client–Server (tách biệt để phát triển độc lập), Stateless (mỗi request tự đủ thông tin, giúp mở rộng ngang), Cacheable (response tự đánh dấu cache để tăng hiệu năng), Uniform Interface (giao diện thống nhất với 4 thành phần con: định danh tài nguyên, thao tác qua biểu diễn, thông điệp tự mô tả, HATEOAS — đây là ràng buộc trung tâm), Layered System (phân tầng để tiến hóa hạ tầng mà không phá client), và Code on Demand (tùy chọn, ít dùng).
Là BA, bạn không cần tự dựng API, nhưng bạn cần đủ năng lực để đọc spec, soi vi phạm, và đối thoại có trọng lượng với đội kỹ thuật lẫn đối tác. Hãy nắm chắc nhất hai ràng buộc Stateless và Uniform Interface, vì chúng ảnh hưởng trực tiếp nhất tới khả năng mở rộng và chi phí tích hợp. Ở các bài tiếp theo, bạn sẽ đi sâu vào từng mảnh ghép cụ thể của REST như phương thức HTTP, cấu trúc URL, và mã trạng thái — tất cả đều là hiện thân của những nguyên tắc bạn vừa học ở đây.