The only entry point for client.
INCOMING REQUEST
│
▼
┌─────────────────┐
│ API GATEWAY │
│ │
│ 1. Read path │
│ 2. Match route │
│ 3. Apply filter│
│ 4. Forward │
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
Path matches? Path matches? Path matches?
/api/auth/login /api/auth/** /api/tasks/**
│ │ │
▼ ▼ ▼
No filter AuthFilter AuthFilter
│ │ │
▼ ▼ ▼
auth-service auth-service task-service
:8081 :8081 :8082
Create another Spring Boot project: api-gateway
Dependencies
spring-cloud-starter-gatewayspring-cloud-starter-netflix-eureka-clientSetup application.yml
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 🔥 important
# Global CORS configuration
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowedHeaders: "*"
exposedHeaders:
- X-Trace-Id
# Default filters applied to all routes
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE
- AddRequestHeader=X-Gateway-Timestamp, ${spring.cloud.gateway.timestamp:0}
# routes are in order (go first match)
routes:
# Auth Service - Public endpoints (no JWT validation)
- id: auth-service-public
uri: <http://localhost:8081>
predicates:
- Path=/api/auth/login, /api/auth/register, /api/auth/refresh
filters:
- RewritePath=/api/auth/(?<segment>.*), /api/auth/${segment}
# Task Service - All endpoints require authentication
- id: task-service
uri: <http://localhost:8082>
predicates:
- Path=/api/tasks/**
filters:
- AuthenticationFilter
- RewritePath=/api/tasks/(?<segment>.*), /api/tasks/${segment}
# Orchestrator Service (internal, usually not exposed)
- id: orchestrator-service
uri: <http://localhost:8083>
predicates:
- Path=/api/orchestrator/**
filters:
- AuthenticationFilter
eureka:
client:
service-url:
defaultZone: <http://localhost:8761/eureka>
# Logging
logging:
level:
root: INFO
com.taskplatform.gateway: DEBUG
org.springframework.cloud.gateway: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId:-}] %-5level %logger{36} - %msg%n"
# Resilience4j Circuit Breaker
resilience4j:
circuitbreaker:
instances:
authService:
slidingWindowSize: 10
failureRateThreshold: 50
waitDurationInOpenState: 10000
permittedNumberOfCallsInHalfOpenState: 3
timelimiter:
instances:
authService:
timeoutDuration: 3s
Logical flow:
Client send request to API Gateway: Path = /api/tasks
Match against predicates (in order)
Apply filters for this route
AuthenticationFilter executes (validate token)
Add headers to request
X-User-Id: 550e8400-e29b-41d4-a716-446655440000 │ │
X-User-Name: testuser │ │
X-User-Roles: ROLE_USER
If auth passes, forward to uri: http://localhost:8082