Tampilan
Kenapa penting
Setelah sistem dipecah menjadi beberapa service, client tidak boleh dipaksa mengenal alamat order-service, payment-service, dan inventory-service satu per satu. Tanpa satu pintu masuk, setiap service harus memverifikasi token sendiri, mengatur CORS sendiri, dan membatasi traffic sendiri — dengan implementasi yang pelan-pelan berbeda. API gateway menjadi pintu depan tunggal yang menerima semua request dari luar, menerapkan kebijakan lintas-service, lalu meneruskannya ke service yang tepat. Hampir semua sistem microservices yang akan kamu temui di tempat kerja punya komponen ini, entah Spring Cloud Gateway, Kong, NGINX, atau gateway bawaan cloud — konsepnya sama.
Konsep inti
Peran gateway: kebijakan di tepi, bukan logika bisnis
Gateway duduk di antara client (browser, aplikasi mobile, partner) dan service internal:
text
client ──HTTPS──▶ API gateway ──▶ order-service
│ ──▶ payment-service
│ ──▶ inventory-service
└─ auth, rate limit, CORS, logging, routing| Tugas | Contoh |
|---|---|
| Routing | /api/orders/** → order-service, /api/payments/** → payment-service |
| Auth di tepi | tolak request tanpa JWT valid sebelum menyentuh service |
| Rate limiting | maksimal 10 request/detik per user untuk endpoint checkout |
| CORS | satu tempat untuk mengizinkan origin https://toko.example.com |
| Transformasi ringan | tambah header X-Request-Id, buang prefix path |
| Agregasi ringan | jarang dan hati-hati — biasanya lebih tepat di BFF (lihat di bawah) |
Yang tidak boleh ada di gateway: aturan diskon, validasi stok, perhitungan ongkir, atau query database milik service lain. Gateway dilewati semua traffic, sehingga logika bisnis di sana membuat setiap perubahan aturan bisnis memerlukan deploy komponen paling kritis — kamu kembali punya monolit, hanya bentuknya lain.
Auth di tepi tetap butuh auth di service
Gateway menolak request anonim lebih awal, tetapi service tetap memvalidasi token yang diterimanya (defense in depth). Jangan mengandalkan "toh sudah lewat gateway" — traffic internal bisa datang dari jalur lain.
WebMVC atau WebFlux?
Sejak Spring Cloud 2025.0, Spring Cloud Gateway tersedia dalam dua varian server dengan starter yang namanya eksplisit. Di Spring Cloud 2025.1 (dipakai bersama Spring Boot 4.x) nama starter lama sudah tidak dipakai lagi.
| Gateway Server WebMVC | Gateway Server WebFlux | |
|---|---|---|
| Starter | spring-cloud-starter-gateway-server-webmvc | spring-cloud-starter-gateway-server-webflux |
| Model | servlet, blocking (cocok dengan virtual threads) | reaktif, Netty, non-blocking |
| Prefix properti route | spring.cloud.gateway.server.webmvc.routes | spring.cloud.gateway.server.webflux.routes |
| Rate limiter bawaan | filter RateLimit (berbasis Bucket4j) | RequestRateLimiter + Redis (spring-boot-starter-data-redis-reactive) |
| Cocok bila | tim sudah nyaman dengan Spring MVC | butuh ribuan koneksi long-lived, sudah familiar Reactor |
xml
<!-- Potongan pom.xml: versi diatur oleh BOM spring-cloud-dependencies 2025.1.x -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>Untuk jalur belajar ini, contoh memakai WebFlux karena rate limiter Redis-nya paling banyak dipakai di industri. Konsep route, predicate, dan filter sama di kedua varian; nama dan bentuk beberapa filter berbeda — selalu cocokkan dengan halaman dokumentasi varian yang kamu pakai.
Route, predicate, filter
Sebuah route terdiri dari:
id— nama unik route;uri— tujuan, mis.http://order-service:8080;predicates— syarat request agar cocok dengan route ini (path, method, header, host, dsb.);filters— modifikasi request/response sebelum dan sesudah diteruskan.
yaml
# Potongan application.yml gateway (WebFlux)
spring:
cloud:
gateway:
server:
webflux:
routes:
- id: orders
uri: http://order-service:8080
predicates:
- Path=/api/orders/**
- Method=GET,POST
filters:
- StripPrefix=1
- AddRequestHeader=X-Gateway, jalur-java
- id: payments
uri: http://payment-service:8080
predicates:
- Path=/api/payments/**
filters:
- StripPrefix=1Request GET /api/orders/42 cocok dengan route orders; StripPrefix=1 membuang segmen /api, sehingga order-service menerima GET /orders/42 plus header X-Gateway. Route dievaluasi berurutan dan request yang tidak cocok dengan route mana pun mendapat 404 dari gateway.
Rate limiting dengan Redis
RequestRateLimiter memakai algoritma token bucket: setiap kunci (mis. ID user) punya bucket berisi token; satu request memakan satu token; token terisi ulang dengan laju tetap. Redis menyimpan state bucket sehingga semua instance gateway berbagi hitungan yang sama.
Logikanya bisa kamu lihat dalam simulasi Java murni berikut (kapasitas 3, isi ulang 1 token/detik):
java
public class TokenBucketDemo {
static final class TokenBucket {
private final int capacity; // burstCapacity
private final int refillPerSecond; // replenishRate
private double tokens;
private long lastRefillMillis;
TokenBucket(int capacity, int refillPerSecond, long nowMillis) {
this.capacity = capacity;
this.refillPerSecond = refillPerSecond;
this.tokens = capacity;
this.lastRefillMillis = nowMillis;
}
boolean tryConsume(long nowMillis) {
double elapsedSeconds = (nowMillis - lastRefillMillis) / 1000.0;
tokens = Math.min(capacity, tokens + elapsedSeconds * refillPerSecond);
lastRefillMillis = nowMillis;
if (tokens >= 1) {
tokens -= 1;
return true;
}
return false;
}
}
public static void main(String[] args) {
TokenBucket bucket = new TokenBucket(3, 1, 0);
long[] requestTimes = {0, 100, 200, 300, 400, 1500, 1600};
for (long t : requestTimes) {
String status = bucket.tryConsume(t) ? "200 OK" : "429 Too Many Requests";
System.out.println("t=" + t + "ms -> " + status);
}
}
}text
t=0ms -> 200 OK
t=100ms -> 200 OK
t=200ms -> 200 OK
t=300ms -> 429 Too Many Requests
t=400ms -> 429 Too Many Requests
t=1500ms -> 200 OK
t=1600ms -> 429 Too Many RequestsTiga request pertama memakai isi bucket (burst). Setelah itu hanya ada pecahan token, sampai cukup waktu berlalu. Di gateway, konfigurasinya seperti ini:
yaml
# Potongan: butuh spring-boot-starter-data-redis-reactive dan Redis yang berjalan
routes:
- id: checkout
uri: http://order-service:8080
predicates:
- Path=/api/checkout/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5
redis-rate-limiter.burstCapacity: 10
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"key-resolver menunjuk bean yang menentukan "bucket milik siapa":
java
// Potongan konfigurasi gateway WebFlux
@Configuration
class RateLimitConfig {
@Bean
KeyResolver userKeyResolver() {
return exchange -> exchange.getPrincipal()
.map(Principal::getName)
.defaultIfEmpty("anonymous");
}
}Request yang ditolak mendapat 429 Too Many Requests. Hati-hati dengan default: kunci kosong ditolak (403) kecuali kamu mengatur spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key.
Token relay
Bila gateway berperan sebagai OAuth2 client (user login lewat gateway), filter TokenRelay meneruskan access token user sebagai header Authorization: Bearer ... ke service tujuan. Service lalu memvalidasinya sebagai resource server. Butuh spring-boot-starter-oauth2-client di gateway.
yaml
filters:
- TokenRelay=
- StripPrefix=1Detail OAuth2 antar-service dibahas di bab security terdistribusi; yang penting sekarang: gateway meneruskan token, bukan membuat token palsu atau mengganti identitas user.
Gateway vs BFF
BFF (Backend for Frontend) adalah service biasa yang dibuat khusus untuk satu jenis client — mis. mobile-bff yang menggabungkan profil, pesanan terakhir, dan poin loyalty menjadi satu respons layar beranda.
| API gateway | BFF | |
|---|---|---|
| Jumlah | biasanya satu (per lingkungan) | satu per jenis client |
| Isi | kebijakan teknis lintas-service | agregasi & bentuk respons per layar |
| Pemilik | tim platform | tim frontend/client terkait |
| Logika bisnis | tidak | boleh ringan (orkestrasi tampilan), bukan aturan domain inti |
Urutannya umum: client → gateway → BFF → service domain.
Contoh di dunia backend
Toko online punya order-service, payment-service, dan catalog-service. Kebutuhan: katalog boleh diakses anonim, checkout wajib login dan dibatasi 5 request/detik per user, origin web resmi saja yang boleh lewat CORS.
yaml
# Potongan application.yml api-gateway (Spring Cloud 2025.1, varian WebFlux)
spring:
data:
redis:
host: redis
port: 6379
cloud:
gateway:
server:
webflux:
globalcors:
cors-configurations:
"[/**]":
allowed-origins: "https://toko.example.com"
allowed-methods: GET,POST,PUT,DELETE
allowed-headers: "*"
default-filters:
- AddResponseHeader=X-Content-Type-Options, nosniff
routes:
- id: catalog
uri: http://catalog-service:8080
predicates:
- Path=/api/products/**
- Method=GET
filters:
- StripPrefix=1
- id: checkout
uri: http://order-service:8080
predicates:
- Path=/api/checkout/**
filters:
- TokenRelay=
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@userKeyResolver}"
- id: payments
uri: http://payment-service:8080
predicates:
- Path=/api/payments/**
filters:
- TokenRelay=
- StripPrefix=1java
// Potongan: security gateway WebFlux — katalog publik, sisanya wajib login
@Configuration
@EnableWebFluxSecurity
class GatewaySecurityConfig {
@Bean
SecurityWebFilterChain gatewaySecurity(ServerHttpSecurity http) {
return http
.authorizeExchange(ex -> ex
.pathMatchers(HttpMethod.GET, "/api/products/**").permitAll()
.anyExchange().authenticated())
.oauth2Login(Customizer.withDefaults())
.build();
}
}Perhatikan apa yang tidak ada: tidak ada perhitungan total belanja, tidak ada cek stok. Gateway hanya memutuskan siapa boleh lewat, seberapa sering, dan ke mana. Alamat http://order-service:8080 masih ditulis tetap — di bab berikutnya alamat ini diganti dengan service discovery (lb://order-service).
Pertanyaan interview
1. Apa saja tanggung jawab API gateway, dan apa yang sebaiknya tidak ditaruh di sana?
Gateway menangani hal teknis lintas-service: routing, autentikasi di tepi, rate limiting, CORS, header keamanan, logging/korelasi request, dan transformasi ringan seperti membuang prefix path. Logika bisnis — harga, diskon, stok, aturan domain — tetap di service pemiliknya. Gateway dilewati semua traffic; menaruh logika bisnis di sana membuat coupling tinggi dan setiap perubahan aturan bisnis memerlukan deploy komponen paling kritis.
2. Bagaimana rate limiting di Spring Cloud Gateway bekerja ketika gateway berjalan beberapa instance?
RequestRateLimiter dengan RedisRateLimiter memakai token bucket yang state-nya disimpan di Redis, jadi semua instance gateway membaca dan mengurangi bucket yang sama per kunci. replenishRate adalah laju isi ulang per detik, burstCapacity kapasitas maksimum untuk lonjakan, dan KeyResolver menentukan kunci (ID user, API key, IP). Request yang melebihi batas mendapat 429. Tanpa penyimpanan bersama, tiap instance menghitung sendiri dan batas efektifnya menjadi N kali lipat.
3. Apa beda API gateway dengan BFF?
Gateway adalah pintu masuk umum untuk semua client dan berisi kebijakan teknis. BFF adalah service yang dibuat untuk satu jenis client (web, mobile, partner) dan melakukan agregasi serta pembentukan respons yang dibutuhkan layar client itu. BFF biasanya dimiliki tim client terkait dan berada di belakang gateway.
Jebakan umum
- Memakai nama starter lama atau prefix
spring.cloud.gateway.routesdi Spring Cloud 2025.1 — route tidak terbaca dan semua request berakhir404. Pakai prefixspring.cloud.gateway.server.webflux/...server.webmvc. - Mencampur varian: menambah
spring-boot-starter-web(servlet) ke gateway WebFlux, atau menyalin konfigurasi filter WebFlux ke gateway WebMVC. Pilih satu varian dan ikuti dokumentasinya. - Lupa
StripPrefix, sehingga service menerima/api/orders/42padahal controller-nya memetakan/orders/{id}. - Rate limit per IP di belakang load balancer — semua request terlihat dari IP yang sama. Pakai ID user atau API key sebagai kunci, atau ambil IP asli dari header tepercaya.
- Service internal menerima request tanpa memvalidasi token karena "sudah dicek gateway".
- Menaruh aturan bisnis atau agregasi berat di filter gateway — gateway jadi lambat, sulit dites, dan menjadi bottleneck deploy.
Ringkasan
- API gateway adalah pintu masuk tunggal: routing, auth di tepi, rate limit, CORS, dan transformasi ringan.
- Spring Cloud 2025.x punya dua varian:
spring-cloud-starter-gateway-server-webmvcdanspring-cloud-starter-gateway-server-webflux, dengan prefix propertispring.cloud.gateway.server.<varian>. - Route =
id+uri+predicates+filters; request tanpa route cocok mendapat404. RequestRateLimiter+ Redis memakai token bucket bersama (replenishRate,burstCapacity,KeyResolver) dan membalas429saat batas terlewati.TokenRelaymeneruskan access token user ke service; service tetap memvalidasinya.- Agregasi per jenis client → BFF; logika bisnis → service domain, bukan gateway.