+2

API Design phải biết dành cho website developer

1. Ký số (Signature)

  • Khái niệm: Để ngăn chặn dữ liệu trong API bị giả mạo, chúng ta thường phải triển khai cơ chế ký số cho API.

  • Cách hoạt động:

    • Bên phía requester:
      • Ghép các tham số request, timestampsecret key thành một chuỗi.
      • Tạo chữ ký (sign) bằng thuật toán hash như MD5.
      • Gửi chữ ký này trong request parameters hoặc headers.
    • Bên phía API gateway:
      • Lấy giá trị sign từ request.
      • Dùng cùng các tham số request, timestampsecret key để tạo một chữ ký mới.
      • So sánh hai giá trị sign.
      • Nếu khớp -> request hợp lệ, tiếp tục xử lý.
      • Nếu không khớp -> trả về lỗi chữ ký không hợp lệ.
  • Tại sao cần timestamp trong chữ ký?

    • Tránh replay attack (gửi lại request cũ).
    • Giảm nguy cơ bị brute-force dò secret key.
    • Thường giới hạn thời gian hiệu lực request (VD: 15 phút).
  • Cách tạo secret key:

    • Dùng private key cố định: Cả client & server dùng chung một secret key cố định.
    • Dùng cặp AK/SK: Client gửi AK trong request header. Server tra cứu SK theo AK rồi dùng để kiểm tra chữ ký.

2. Mã hóa dữ liệu

  • Khái niệm: Với dữ liệu nhạy cảm (password, số thẻ ngân hàng, số tiền giao dịch...), không thể gửi dưới dạng plaintext qua internet.

  • Usecase:

    • Ex: API đăng ký tài khoản:

      • Frontend: Mã hóa mật khẩu bằng AES với public key.
      • Backend: Giải mã bằng secret key, xử lý đăng ký, rồi lưu vào DB với cơ chế mã hóa khác (VD: bcrypt).

3. Danh sách IP cho phép (IP Whitelisting)

  • Khái niệm: Giả sử chữ ký API hoặc mã hóa bị lộ, kẻ tấn công vẫn phải gửi request từ một IP hợp lệ.

  • Cách triển khai:

    • Chỉ cho phép request từ các IP trong danh sách whitelist. Nếu IP không hợp lệ -> từ chối request ngay lập tức.
    • Lưu ý: Nếu server nội bộ bị xâm nhập, hacker vẫn có thể gửi request từ IP hợp lệ.
    • Giải pháp bổ sung: Sử dụng Web Firewall (VD: ModSecurity).

4. Giới hạn tốc độ (Rate Limiting)

  • Khái niệm: API phải giới hạn số request để tránh bị spam gây quá tải.

  • Các cách giới hạn phổ biến:

    • Giới hạn theo IP: Ví dụ mỗi IP tối đa 10.000 request/phút.
    • Giới hạn theo endpoint: Ví dụ một IP chỉ được gọi 2.000 request/phút vào một API cụ thể.
    • Giới hạn theo AK/SK: Ví dụ một user với AK/SK chỉ được gọi 10.000 request/phút.
  • Triển khai với:

    • Nginx (limit_req_module)
    • Redis (rate limiting với Lua script)
    • API Gateway

5. Kiểm tra tham số (Parameter Validation)

  • Khái niệm: Mọi request API đều phải kiểm tra tham số đầu vào để tránh lỗi hoặc tấn công dữ liệu.

  • Ví dụ cần kiểm tra:

    • Trường bắt buộc không được để trống.
    • Kiểu dữ liệu (string, int, date...).
    • Độ dài tối đa.
    • Giá trị hợp lệ cho các trường enum.
  • Sai sót phổ biến:

    • Giá trị amount chấp nhận số âm -> gây lỗi tài chính.
    • Trường status nhận giá trị không xác định -> lỗi logic hệ thống.

6. Chuẩn hóa format response

  • Khái niệm: Response API phải có format thống nhất, tránh gây khó khăn cho người tích hợp.

  • Ví dụ xấu:
    { "rt": 10, "errorMgt": "No permission", "result": null }

  • Cách đúng:
    { "code": 403, "message": "No permission", "data": null }

    => Giữ format code + message + data cố định cho mọi response.

7. Xử lý lỗi thống nhất

  • Khái niệm: API không bao giờ được trả về lỗi hệ thống chi tiết, tránh lộ thông tin nhạy cảm.

  • Ví dụ xấu:
    { "error": "SQL Syntax Error at line 23: SELECT * FROM users WHERE ..." }

  • Cách đúng:
    { "code": 500, "message": "Internal Server Error", "data": null }

=> Log đầy đủ lỗi nội bộ, nhưng chỉ trả về thông báo chung cho client.

8. Ghi log request

  • Để debug API dễ dàng, cần log lại:
    • URL request
    • Tham số
    • Headers
    • Phương thức HTTP
    • Response
    • Thời gian xử lý
    • Thêm traceId để liên kết các log của cùng một request -> dễ dàng tra cứu.

9. Đảm bảo tính idempotent

  • Nếu một request được gửi nhiều lần, API phải tránh xử lý trùng lặp.

  • Cách xử lý:

    • Dùng unique constraint trong DB để ngăn insert trùng.
    • Dùng Redis để lưu requestId, nếu request trùng thì từ chối xử lý.

10. Giới hạn số bản ghi trong API batch

  • Batch API không nên cho phép gửi quá nhiều bản ghi một lúc -> dễ gây quá tải.

  • Giới hạn hợp lý:

    • Mỗi request tối đa 500 bản ghi.
    • Nếu gửi quá số này -> trả về lỗi.

11. Test tải trước khi triển khai

  • Ngay cả khi có rate limit, API vẫn cần stress test để kiểm tra khả năng chịu tải thực tế.

  • Dùng các công cụ như:

    • JMeter
    • Apache Bench (ab)

12. Xử lý bất đồng bộ

  • Những API xử lý lâu (VD: batch job) không nên chạy đồng bộ.

  • Giải pháp:

    • Đẩy task vào message queue (Kafka, RabbitMQ).
    • Trả về ngay response success.
    • Để client check kết quả qua callback hoặc polling.
    • Sử dụng WebSocket hoặc SocketIO

13. Ẩn dữ liệu nhạy cảm

  • Một số API response chứa dữ liệu cần che bớt, như:

    • Số điện thoại -> 098****1234
    • Số thẻ ngân hàng -> 5196****1234

14. Tài liệu API đầy đủ

  • Một API tốt cần có tài liệu rõ ràng:

    • URL
    • Method (GET, POST...)
    • Request params
    • Response format
    • Error codes
    • Ví dụ request
  • Chuẩn hóa quy ước đặt tên
    Để duy trì tính nhất quán:

    • Sử dụng camelCase cho tên trường.
    • Chuẩn hóa kiểu trường & độ dài (ví dụ: id là Long, status là int).
    • Xác định định dạng thời gian thống nhất (ví dụ: yyyy-MM-dd HH:mm:ss).
    • Tài liệu cũng nên chỉ định cách sử dụng khóa AK/SK và tên miền API.

15. Chọn đúng phương thức HTTP

  • GET: Đọc dữ liệu, không cần tham số body.

  • POST: Gửi dữ liệu, mở rộng dễ dàng.

  • PUT: Cập nhật dữ liệu.

  • DELETE: Xóa dữ liệu.

  • Ưu tiên POST hơn GET vì:

    • GET có giới hạn độ dài URL (~5000 ký tự).
    • GET dễ bị cache sai dữ liệu.

16. Dùng request headers đúng cách

  • Các tham số quan trọng như token, traceId nên để trong request header thay vì query string (trong body hay params).
  • Server có thể xử lý các thứ trên thông qua interceptor,tương tự với FE.

17. Thiết kế API theo nguyên tắc Single Responsibility

  • Tránh API quá đa năng, chứa nhiều logic khác nhau.
  • Chia nhỏ API theo use case cụ thể, dễ bảo trì hơn.
  • Ví dụ: Thay vì thiết kế một API để xử lý mọi thứ, hãy tách chúng ra:
    • API web:
/web/v1/order/create
/web/v1/order/fastCreate
  • API mobile
/mobile/v1/order/create
/mobile/v1/order/fastCreate
  • Điều này tạo ra bốn API riêng biệt, mỗi API dành riêng cho một trường hợp sử dụng cụ thể.

  • Lợi ích:

    • Logic kinh doanh vẫn rõ ràng.
    • API dễ bảo trì hơn.
    • Ít có nguy cơ xảy ra tác dụng phụ không mong muốn khi thực hiện thay đổi.

18. Batch Processing

  • API nên hỗ trợ batch processing thay vì xử lý từng record riêng lẻ.

  • Ví dụ sai:

/getOrder?id=1

→ Gọi 1,000 lần nếu cần 1,000 đơn hàng.

  • Ví dụ đúng:
/getOrders?ids=[1,2,3,...]

→ Trả về một lần, tiết kiệm tài nguyên.

  • Tương tự với insert/update/delete → Nên hỗ trợ batch để tối ưu hiệu suất.

Kết luận

Tuân thủ những best practice này sẽ giúp API an toàn, hiệu năng cao và dễ bảo trì. 💪🚀


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí