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

API Gateway

  1. Create another Spring Boot project: api-gateway

  2. Dependencies

  3. Setup 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:

  1. Client send request to API Gateway: Path = /api/tasks

  2. Match against predicates (in order)

  3. Apply filters for this route

    1. AuthenticationFilter is configured
  4. AuthenticationFilter executes (validate token)

  5. Add headers to request

    X-User-Id: 550e8400-e29b-41d4-a716-446655440000             │    │
    X-User-Name: testuser                                       │    │
    X-User-Roles: ROLE_USER  
    
  6. If auth passes, forward to uri: http://localhost:8082