Trang chủ / Thiết Kế & Kiến Trúc Phần Mềm
Bài học lập trình từ tệp trắng: Giải mã API, REST và cách ứng dụng hiện đại trao đổi với nhau
Bắt đầu từ một tệp trắng và một mục tiêu đơn giản: để một chương trình sử dụng dữ liệu hoặc tính năng của chương trình khác. Nhu cầu quen thuộc này chính là nguồn gốc của API. Trong phần mềm hiện đại, hầu hết các nhóm đều dựa vào API web được xây dựng quanh REST để kết nối ứng dụng, dịch vụ và thiết bị. Nếu bạn từng thắc mắc api rest hoạt động kết nối ứng dụng như thế nào, bài viết này sẽ giải thích một cách rõ ràng và thực tế.
API thực chất là gì
API, hay Application Programming Interface, là một bản hợp đồng mô tả cách một phần mềm có thể giao tiếp với phần mềm khác. Hãy hình dung nó như thực đơn trong nhà hàng: bạn không cần biết bếp hoạt động ra sao, chỉ cần biết mình có thể gọi món gì và gọi như thế nào. Trong mã nguồn, API định nghĩa các thao tác có sẵn, cấu trúc dữ liệu và quy tắc để yêu cầu kết quả.
API tồn tại ở nhiều cấp độ. Trong một chương trình, bạn có thể dùng API thư viện. Giữa các tiến trình, bạn có thể dùng API hệ điều hành. Trên môi trường web, bạn dùng API mạng. Ý tưởng cốt lõi trong mọi trường hợp đều giống nhau: một ranh giới ổn định che giấu chi tiết bên trong nhưng vẫn cung cấp các chức năng hữu ích.
REST trong một đoạn
REST, hay Representational State Transfer, là một phong cách thiết kế API mạng. REST API cung cấp các tài nguyên (resource), là những thứ như người dùng, đơn hàng hoặc sản phẩm. Mỗi tài nguyên có một địa chỉ gọi là URL, và bạn tương tác với nó bằng các phương thức HTTP tiêu chuẩn: GET để đọc, POST để tạo mới, PUT hoặc PATCH để cập nhật và DELETE để xóa. Phản hồi thường được trả về dưới dạng JSON, một định dạng văn bản nhẹ mà cả con người và máy đều có thể phân tích dễ dàng.
Mô hình tư duy đơn giản
Hãy hình dung hai ứng dụng: một ứng dụng di động và một máy chủ. Ứng dụng cần danh sách các mục. Nó gửi một yêu cầu HTTP đến một URL đã biết, ví dụ /api/items. Máy chủ đọc yêu cầu, kiểm tra quyền, lấy hoặc cập nhật dữ liệu và trả về phản hồi gồm mã trạng thái và phần thân. Sau đó ứng dụng hiển thị dữ liệu lên màn hình. Vòng trao đổi này chính là nhịp đập của hầu hết hệ thống hiện đại.
Các khối cơ bản của một yêu cầu HTTP
- Method: Hành động bạn muốn thực hiện, như GET, POST, PUT hoặc DELETE.
- URL: Địa chỉ của tài nguyên, ví dụ /api/items/42.
- Headers: Siêu dữ liệu như loại nội dung, token xác thực và gợi ý bộ nhớ đệm.
- Body: Phần dữ liệu gửi kèm, thường có trong yêu cầu POST và PUT, thường là JSON.
Ví dụ, để tạo một mục mới, client có thể gửi POST tới /api/items kèm thân JSON chứa các trường như name và price. Máy chủ sẽ kiểm tra dữ liệu đầu vào, lưu trữ và phản hồi bằng tài nguyên mới cùng mã trạng thái 201 Created.
Điều gì khiến REST trở nên “RESTful”
REST không phải là một tiêu chuẩn cứng nhắc; nó là tập hợp các ràng buộc định hướng. Một dịch vụ được coi là RESTful khi tuân theo các thực hành phổ biến sau:
- Tài nguyên là danh từ: Dùng đường dẫn như /users và /orders thay vì động từ như /getUsers.
- Phương thức HTTP đảm nhiệm động từ: GET để đọc, POST để tạo, PUT để thay thế, PATCH để cập nhật một phần, DELETE để xóa.
- Phi trạng thái (Statelessness): Mỗi yêu cầu chứa đầy đủ thông tin cần thiết để xử lý. Máy chủ không phụ thuộc vào trạng thái phiên của client giữa các lần gọi.
- Mã trạng thái rõ ràng: 200 cho thành công, 201 cho tạo mới, 400 cho dữ liệu đầu vào không hợp lệ, 401 cho chưa xác thực, 404 cho không tìm thấy, 500 cho lỗi máy chủ.
- Định dạng dữ liệu nhất quán: JSON là lựa chọn phổ biến nhất hiện nay.
Những lựa chọn này giúp hệ thống dễ hiểu, dễ lưu đệm, dễ mở rộng và dễ gỡ lỗi hơn.
Đi qua một quy trình nhỏ
Hãy xét một dịch vụ quản lý công việc đơn giản. Ứng dụng bắt đầu bằng cách gọi GET /api/todos để liệt kê các tác vụ. Mỗi tác vụ có id, title và cờ done. Để thêm tác vụ, ứng dụng gửi POST /api/todos với thân JSON như {“title”:”Learn APIs”}. Máy chủ phản hồi bằng đối tượng tác vụ đầy đủ, bao gồm id mới. Để đánh dấu hoàn thành, ứng dụng gửi PATCH /api/todos/17 với {“done”:true}. Nếu không cần tác vụ nữa, nó gửi DELETE /api/todos/17. Mỗi bước đều là một cặp yêu cầu và phản hồi đơn giản, dễ đọc.
Quản lý phiên bản và tính ổn định
API thực tế luôn phát triển. Các nhóm bổ sung trường dữ liệu, thay đổi hành vi và loại bỏ endpoint cũ. Để tránh làm hỏng client hiện có, nhiều dịch vụ áp dụng quản lý phiên bản. Cách phổ biến là đưa phiên bản vào URL, ví dụ /api/v2/orders. Cách khác là dùng header Accept để yêu cầu một phiên bản cụ thể. Việc quản lý phiên bản bảo vệ người dùng hiện tại trong khi vẫn cho phép bạn cải tiến.
Xác thực và phân quyền
Hầu hết API cần biết ai đang gọi và họ được phép làm gì. Hai mẫu phổ biến là:
- API keys: Token đơn giản được truyền qua header. Phù hợp cho giao tiếp giữa các máy chủ với kiểm soát truy cập cơ bản.
- OAuth 2.0: Framework mạnh mẽ hơn cho ủy quyền truy cập, thường dùng khi ứng dụng bên thứ ba cần quyền hạn chế với dữ liệu người dùng.
Một yêu cầu có thể bao gồm header Authorization: Bearer <token>. Máy chủ xác minh token, kiểm tra phạm vi quyền và tiếp tục xử lý hoặc trả về mã 401 hoặc 403.
Lỗi cũng là một phần của hợp đồng
Thông báo lỗi rõ ràng giúp tiết kiệm thời gian. Một API tốt trả về mã trạng thái, loại lỗi mà máy có thể đọc và thông điệp thân thiện với con người. Ví dụ, phản hồi 400 có thể chứa {“error”:”invalid_input”,”details”:”price must be positive”}. Điều này giúp nhà phát triển sửa lỗi nhanh và xây dựng client bền vững hơn.
Phân trang, lọc và sắp xếp
Tập hợp dữ liệu lớn cần giới hạn hợp lý. Thay vì trả về mọi bản ghi, API sử dụng phân trang. Mẫu phổ biến là tham số truy vấn như page và limit, hoặc phân trang dựa trên con trỏ với next_token. Việc lọc và sắp xếp cũng được thể hiện trong URL, ví dụ /api/products?category=books&sort=price_asc. Các cơ chế này giúp phản hồi nhanh và dễ dự đoán hơn.
Tính idempotency và thử lại an toàn
Mạng có thể gặp sự cố. Client sẽ thử lại. Để tránh tác dụng phụ trùng lặp, REST API khuyến khích tính idempotency cho các thao tác như PUT và DELETE. Gửi cùng một yêu cầu hai lần sẽ cho kết quả giống như gửi một lần. Với các lệnh không có tính idempotency như POST, dịch vụ đôi khi chấp nhận header Idempotency-Key để việc thử lại không tạo ra bản ghi trùng.
Bộ nhớ đệm và hiệu năng
REST hoạt động tốt với bộ nhớ đệm HTTP. Máy chủ có thể gửi các header Cache-Control và ETag để client hoặc proxy tái sử dụng phản hồi gần đây. Việc lưu đệm giúp giảm độ trễ và tải hệ thống, đồng thời thường khiến ứng dụng phản hồi nhanh hơn mà không cần thêm mã.
Thiết kế từ tệp trắng
Khi bắt đầu từ con số không, hãy giữ thiết kế lấy con người làm trung tâm. Bắt đầu với những tài nguyên mà người dùng quan tâm. Đặt tên rõ ràng. Ánh xạ hành động tới phương thức HTTP. Trả về mã trạng thái hợp lý. Ghi tài liệu với ví dụ cho các luồng phổ biến. Thêm phân trang ngay từ đầu. Tính đến xác thực ngay từ ngày đầu. Những lựa chọn đơn giản này cộng dồn lại tạo nên một API gọn gàng và dễ bảo trì.
Khi REST không phải là lựa chọn tốt nhất
REST là lựa chọn mặc định mạnh, nhưng không phải là duy nhất. GraphQL cho phép client yêu cầu chính xác các trường cần thiết trong một yêu cầu duy nhất. gRPC sử dụng thông điệp nhị phân cho giao tiếp hiệu năng cao, độ trễ thấp. WebSockets hỗ trợ luồng dữ liệu thời gian thực. Nhiều nhóm dùng REST cho hầu hết thao tác CRUD và chuyển sang công cụ khác khi cần thời gian thực hoặc truy vấn phức tạp.
Kết luận chính
- API là một hợp đồng ổn định cho phép các chương trình cộng tác mà không cần chia sẻ chi tiết nội bộ.
- REST là phong cách thực tế được xây dựng trên phương thức HTTP, đường dẫn tài nguyên rõ ràng và JSON.
- API tốt sử dụng mã trạng thái hợp lý, xử lý lỗi có thể dự đoán và hỗ trợ phân trang cùng bộ nhớ đệm.
- Bảo mật, quản lý phiên bản và tính idempotency giúp hệ thống an toàn và đáng tin cậy khi mở rộng.
Từ tệp trắng đến tích hợp hoạt động được, hành trình là việc thiết lập ranh giới rõ ràng và quy tắc có thể dự đoán. Khi bạn xem API như những hợp đồng và REST như một cách đơn giản, nhất quán để thực hiện các hợp đồng đó, ứng dụng hiện đại sẽ không còn là hộp đen. Chúng trở thành một mạng lưới các dịch vụ nhỏ, dễ hiểu, phối hợp với nhau để mang lại những tính năng mà người dùng thực sự cảm nhận được.
