Nội dung bài viết
Video học lập trình mỗi ngày
Bài viết đưa vào Series: Thời đại AI và cách vượt qua vòng phỏng vấn
Anh em làm backend đã bao giờ bị sếp hỏi: "Endpoint search của mày sao URL dài như đường về quê vậy?"
Tôi đã từng có một endpoint filter sản phẩm trông như thế này:
GET /api/v1/products?category=electronics&brand=samsung&minPrice=500000&maxPrice=5000000&inStock=true&sort=price_asc&color[]=black&color[]=silver&rating=4&page=2
Chưa kể còn phải encode các ký tự đặc biệt. URL lên tới 2,000 ký tự, vượt giới hạn của nhiều browser và server. Và đó là lúc tôi bắt đầu tìm hiểu vấn đề thực sự của GET.
Vấn đề của GET — không phải chuyện nhỏ
Giống như quán phở anh Tý — khách gọi món bằng cách la to từ ngoài cửa. Được thôi nếu chỉ có "một tô đặc biệt". Nhưng nếu khách muốn "không hành, thêm gân, ít mỡ, chan riêng, tương đen để bên cạnh, giá để riêng một tô" thì... hàng xóm cũng nghe hết.
Trong code cũng vậy. GET request có 4 vấn đề cốt lõi:
1. URL có giới hạn độ dài
Browser và server thường chặn URL trên 2,000–8,000 ký tự. Filter phức tạp = URL chết.
2. Dữ liệu nhạy cảm bị lộ trong log
GET /api/v1/users?role=admin&token=abc123&company_id=999
Cái này nằm nguyên trong access log của Nginx, CloudFlare, và mọi proxy trên đường đi. Không ai muốn điều đó.
3. Array và nested object — mỗi nơi một kiểu
# Kiểu 1
?roles[0]=admin&roles[1]=editor
# Kiểu 2
?roles=admin&roles=editor
# Kiểu 3
?roles[]=admin&roles[]=editor
Không có chuẩn. Backend Express parse khác, Spring Boot parse khác, NestJS lại khác nữa.
4. Nested structure — đến tôi cũng không dám viết
Thử tưởng tượng filter theo user.address.city hay order.items[].product.category qua query param. Không thể đọc được.
Workaround cũ: dùng POST để "giả" GET
Vì GET không có body, nhiều team chuyển sang POST:
POST /api/v1/products/search
Content-Type: application/json
{
"category": "electronics",
"brand": ["samsung", "apple"],
"priceRange": { "min": 500000, "max": 5000000 },
"inStock": true,
"sort": "price_asc"
}
Sạch hơn, dễ đọc hơn, không giới hạn độ dài. Nhưng đây là hack, không phải solution. Vấn đề:
- POST không phải idempotent — middleware, proxy, và retry logic không biết đây là read-only
- Cache không hoạt động tự động — bạn phải tự implement cache key từ request body
- Client như Axios hay Fetch không tự retry POST khi timeout, vì sợ tạo duplicate
- CDN và API Gateway mặc định không cache POST
RFC 10008 giới thiệu: HTTP QUERY method
Sau nhiều năm tranh luận, RFC 10008 đặt tên chính thức cho thứ mà cộng đồng vẫn đang làm chui: method QUERY.
QUERY /api/v1/products
Content-Type: application/json
{
"category": "electronics",
"brand": ["samsung", "apple"],
"priceRange": { "min": 500000, "max": 5000000 },
"inStock": true,
"sort": "price_asc"
}
Giống y POST về cú pháp — nhưng semantic hoàn toàn khác:
| GET | POST | QUERY | |
|---|---|---|---|
| Request body | Không khuyến khích | Có | Có |
| Safe (read-only) | Có | Không | Có |
| Idempotent | Có | Không | Có |
| Cacheable | Có | Không | Có (theo body) |
| Bookmark/share URL | Có | Không | Không |
QUERY nói thẳng với proxy, CDN, và middleware: "Tôi chỉ đọc, không ghi, có thể cache, có thể retry."

Trước khi anh em migrate — đọc cái này
Tôi biết anh em đang hứng khởi muốn đổi hết sang QUERY. Nhưng thực tế năm 2026:
Support còn rất hạn chế. Kreya đã hỗ trợ từ v1.20. Nhưng Postman, Insomnia, nhiều API Gateway, và một số proxy vẫn chưa. Dùng trong production ngay lúc này có thể bị chặn ở tầng infrastructure mà bạn không kiểm soát.
Không thay thế GET cho mọi thứ. Nếu user có thể share hoặc bookmark URL filter (e.g. trang tìm kiếm sản phẩm cho người dùng cuối) — vẫn dùng GET. QUERY không có URL state để share.
Cache phức tạp hơn. GET cache theo URL. QUERY phải cache theo URL + request body hash — bạn cần tự implement nếu infrastructure chưa hỗ trợ native.
Khi nào nên dùng?
Anh em làm internal API, GraphQL-like query layer, hoặc B2B data API — nơi client là service khác, không phải browser — thì QUERY là method đúng nghĩa về mặt semantic. Dùng POST để search đang là kỹ thuật nợ — QUERY trả nợ đó.
Còn API public, user-facing, cần SEO hay shareable URL? Tiếp tục GET. Đừng migrate cho vui.
Tóm lại: QUERY không phải silver bullet, nhưng nó lấp đúng cái lỗ mà GET để lại và POST đang lấp sai chỗ. Theo dõi support của infrastructure bạn đang dùng — khi thời điểm chín muồi, switch rất xứng đáng.
Nguồn:
- RFC 10008 – The HTTP QUERY Method
- Kreya 1.20 – HTTP QUERY support
- Manuel – "The new HTTP QUERY method explained" (June 17, 2026)
- RFC 9110 – HTTP Semantics

