API Gateway — Cánh cửa duy nhất của backend hiện đại
Khi hệ thống mới chỉ có 1–2 service, client gọi thẳng API là chuyện bình thường. Nhưng khi lên 5, 10 service, mọi thứ bắt đầu rối: app mobile phải nhớ địa chỉ từng service, mỗi nơi tự lo auth, tự lo CORS, tự lo log. Đó là lúc hệ thống cần một cánh cửa duy nhất — API Gateway.
API Gateway làm gì?
API Gateway là lớp nằm giữa client và các service, đóng vai trò cửa ngõ duy nhất. Có 3 pattern phổ biến:
Gateway Routing — đơn giản nhất: gateway nhận request, nhìn path/header rồi forward về đúng service. Kiểu như bác bảo vệ chỉ đường trong tòa nhà.
Gateway Aggregation — một request từ client được gateway gọi song song nhiều service rồi gộp kết quả lại. Ví dụ màn hình chi tiết đơn hàng: cần thông tin order (order-service), user (user-service), sản phẩm (product-service). Không có gateway, mobile phải gọi 3 lần; có gateway chỉ 1 lần — round-trip giảm đáng kể, rất quan trọng trên mạng di động.
Gateway Offloading — đẩy việc chung cho gateway lo: xác thực, SSL termination, rate limiting, log, tracing. Service chỉ tập trung viết business logic, khỏi ai cũng tự làm auth.
Code thử gateway mini bằng Go
Ý tưởng chính không phức tạp: dùng httputil.ReverseProxy của Go stdlib, wrap trong middleware chain:
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
"time"
)
// routes: map path prefix -> service address
var routes = map[string]string{
"/api/users/": "http://user-service:8081",
"/api/orders/": "http://order-service:8082",
}
func proxy(target string) http.Handler {
u, _ := url.Parse(target)
return httputil.NewSingleHostReverseProxy(u)
}
func main() {
mux := http.NewServeMux()
for prefix, target := range routes {
mux.Handle(prefix, logging(auth(proxy(target))))
}
log.Fatal(http.ListenAndServe(":8080", mux))
}
// auth: middleware kiểm tra token (demo — production dùng JWT/OIDC)
func auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") == "" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
// logging: ghi nhận request + thời gian xử lý
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
})
}
Chưa tới 50 dòng đã có gateway routing + auth + logging chạy được. Nhưng khi hệ thống lớn hơn, đừng tự build — dùng Kong, Envoy, Nginx hoặc managed service (AWS API Gateway) vì chúng đã giải quyết sẵn hàng tá vấn đề bạn chưa nghĩ tới: giới hạn request, retry, circuit breaker, blue-green deploy...
Kinh nghiệm thực tế
1. Đừng biến gateway thành "monolith mới". Nhiều team lười, đổ hết logic vào gateway: validate, transform, business rule... vài tháng sau gateway nặng nề, mỗi lần deploy cả đội hồi hộp. Gateway chỉ nên routing + offloading, không mang business logic.
2. Gateway không phải là nơi duy nhất cần auth. Nhiều người nghĩ "có gateway rồi service khỏi check auth" — sai. Nếu một service bị lộ (internal network bị xâm nhập, hoặc service A gọi thẳng service B), không có auth ở service nghĩa là không có chốt chặn nào. Defense in depth: auth ở gateway cho client, auth ở service cho internal call.
3. Theo dõi độ trễ của gateway. Gateway thêm một hop nữa vào mỗi request; nếu nó xử lý chậm, cả hệ thống chậm theo. Luôn có metrics + alert riêng cho gateway, và đừng quên trace qua từng hop.
4. Khi nào KHÔNG cần gateway. Hệ thống 1–2 service, hoặc traffic nội bộ service-to-service: gọi trực tiếp qua service mesh hoặc gRPC, không cần gateway chen giữa. Thêm một lớp là thêm độ trễ, thêm điểm hỏng (single point of failure), thêm thứ phải vận hành.
Tóm lại
API Gateway không phải viên đạn bạc nhưng gần như bắt buộc khi hệ thống đủ lớn. Bắt đầu từ Gateway Routing, thêm offloading dần, và quan trọng nhất: giữ nó mỏng. Code gateway bằng Go rất dễ, vận hành nó tốt mới là thử thách thật sự.