API Gateway bukan hanya sekedar reverse proxy — ada banyak pola canggih yang bisa diimplementasikan untuk membuat sistem lebih robust dan efisien.
Pola 1: Backend for Frontend (BFF)
Buat gateway khusus per jenis client, bukan satu gateway untuk semua:
Mobile App → BFF Mobile → Microservices
Web App → BFF Web → (sama)
Partner API → BFF Partner → (sama)
// BFF Mobile — response lebih ringkas untuk bandwidth hemat
app.get("/api/mobile/products", async (req, res) => {
const products = await productService.getAll();
// Kirim hanya field yang dibutuhkan mobile
res.json(products.map(p => ({
id: p.id,
name: p.name,
price: p.price,
thumb: p.thumbnail_url // bukan full image
})));
});
// BFF Web — response lebih lengkap
app.get("/api/web/products", async (req, res) => {
const [products, categories, featured] = await Promise.all([
productService.getAll(),
categoryService.getAll(),
productService.getFeatured()
]);
res.json({ products, categories, featured });
});
Pola 2: Circuit Breaker
Cegah cascade failure — jika service down, jangan terus coba panggil:
Status: CLOSED → Semua request diteruskan normal
Status: OPEN → Semua request langsung gagal (tidak panggil service)
Status: HALF-OPEN → Coba 1 request, jika berhasil → CLOSED lagi
Transisi:
CLOSED → (error >= threshold) → OPEN
OPEN → (timeout berlalu) → HALF-OPEN
HALF-OPEN → (berhasil) → CLOSED
HALF-OPEN → (gagal) → OPEN
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = "CLOSED"; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}
async call(fn) {
if (this.state === "OPEN") {
if (Date.now() < this.nextAttempt) {
throw new Error("Circuit breaker OPEN — service tidak tersedia");
}
this.state = "HALF_OPEN";
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (err) {
this.onFailure();
throw err;
}
}
onSuccess() { this.failureCount = 0; this.state = "CLOSED"; }
onFailure() {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = "OPEN";
this.nextAttempt = Date.now() + this.timeout;
}
}
}
Pola 3: Request Aggregation (GraphQL-style)
Gateway menggabungkan data dari beberapa service dalam satu call:
// Tanpa aggregation: client butuh 3 request
// GET /users/42
// GET /orders?userId=42
// GET /recommendations?userId=42
// Dengan aggregation: satu request
app.get("/api/profile/:id", async (req, res) => {
const [user, orders, recs] = await Promise.all([
userService.get(req.params.id),
orderService.getByUser(req.params.id),
recService.getForUser(req.params.id),
]);
res.json({ user, orders, recommendations: recs });
});
Pola 4: API Versioning
/api/v1/users → Legacy format
/api/v2/users → New format dengan field baru
/api/v3/users → Breaking change, field dihapus
// Gateway bisa translate v1 request ke internal format
app.get("/api/v1/users/:id", async (req, res) => {
const user = await userService.get(req.params.id);
// v1 masih pakai snake_case
res.json({
user_id: user.id,
full_name: user.name,
email_address: user.email
});
});
app.get("/api/v2/users/:id", async (req, res) => {
const user = await userService.get(req.params.id);
// v2 pakai camelCase
res.json({ userId: user.id, fullName: user.name, email: user.email });
});
Pola 5: Retry dengan Exponential Backoff
async function callWithRetry(fn, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err) {
if (attempt === maxRetries) throw err;
// Exponential backoff: 1s, 2s, 4s, 8s...
const delay = Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, delay));
}
}
}
Summary pola API Gateway:
| Pola | Masalah yang Diselesaikan |
|---|---|
| BFF | Kebutuhan client yang berbeda-beda |
| Circuit Breaker | Cascade failure saat service down |
| Aggregation | Terlalu banyak round-trip dari client |
| Versioning | Perubahan API tanpa breaking client lama |
| Retry + Backoff | Kegagalan sementara yang bisa di-recover |
🎭 Analogi sehari-hari: Tukang listrik handle MCB di rumah. Korslet di kamar mandi → MCB jeglek (circuit breaker), nyala lain tetap aman. Lampu redup karena tegangan turun → coba nyalain ulang (retry). Beda kebutuhan kamar (dapur butuh daya besar, kamar tidur kecil) → split panel (BFF).
💡 Circuit Breaker rule: Kalau service B mati, Service A jangan terus call B berkali-kali. State circuit breaker:
- Closed = normal call
- Open = service down, langsung fail tanpa call (fail fast)
- Half-open = coba call sekali, kalau OK balik Closed
⚠️ Jebakan klasik:
- Retry tanpa backoff = thundering herd, ngulang error pile-up
- No circuit breaker = cascade failure, 1 service down = semua down
- Versioning lupa = breaking change client lama
- Aggregation terlalu greedy = gateway jadi bottleneck
🎯 Pattern stack untuk resilience:
- Timeout wajib (jangan tunggu selamanya)
- Retry + exponential backoff untuk transient failure
- Circuit breaker untuk persistent failure
- Bulkhead isolation pool antar service
- Fallback response saat semua gagal
TL;DR: API Gateway pattern = BFF (per-client), Circuit Breaker (fail fast), Aggregation (combine calls), Versioning (backwards compat), Retry+Backoff (transient recovery). Stack pattern bersama = resilience mature.