Spring Cloud Gateway & Feign Dependencies
Learn how to route microservice traffic through a single API gateway and call services declaratively using Spring Cloud Gateway and OpenFeign.
Introduction
Two problems come up constantly once you have more than one microservice: how do external clients reach the right service without knowing your internal topology, and how do your services call each other without writing repetitive RestTemplate boilerplate? Spring Cloud Gateway and OpenFeign solve these two problems respectively.
- What an API gateway does and why microservices systems need one.
- How to configure routes with spring-cloud-starter-gateway.
- How to write a declarative REST client with spring-cloud-starter-openfeign.
- How gateway and Feign fit into the rest of your Spring Cloud stack.
Why a Gateway and a Declarative Client?
Use case: external clients (browsers, mobile apps) should not need to know that "orders" live on one service and "payments" on another — a gateway presents one unified entry point and routes internally. Meanwhile, service-to-service calls (order-service calling payment-service) are far cleaner as a typed interface than as manual HTTP client code.
API Gateway: spring-cloud-starter-gateway
spring-cloud-starter-gateway builds a reactive API gateway that sits in front of your microservices, routing incoming requests to the correct backend based on path, host, or other predicates. It can also apply cross-cutting concerns like rate limiting, authentication, and request/response rewriting.
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId></dependency>Routes can be defined in application.yml. This example routes any request under /orders/** to order-service, and any request under /payments/** to payment-service — both resolved dynamically through Eureka using the lb:// (load-balanced) scheme.
spring: cloud: gateway: routes: - id: order-service-route uri: lb://order-service predicates: - Path=/orders/** - id: payment-service-route uri: lb://payment-service predicates: - Path=/payments/**Click Run to see what this code prints.
The lb:// scheme depends on a discovery client (such as spring-cloud-starter-netflix-eureka-client) being on the classpath so the gateway can resolve service names to live instances. Without discovery, you can still route to a fixed URI directly.
Declarative Clients: spring-cloud-starter-openfeign
spring-cloud-starter-openfeign lets you define an HTTP client as a plain Java interface annotated with @FeignClient. Spring generates the implementation at runtime, so calling another service looks like calling a local method.
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId></dependency>Enable Feign clients on your main application class, then declare the interface.
@SpringBootApplication@EnableFeignClientspublic class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); }}@FeignClient(name = "payment-service")public interface PaymentClient {
@PostMapping("/payments") PaymentResponse createPayment(@RequestBody PaymentRequest request);}@Servicepublic class OrderService {
private final PaymentClient paymentClient;
public OrderService(PaymentClient paymentClient) { this.paymentClient = paymentClient; }
public void checkout(String orderId, double amount) { PaymentResponse response = paymentClient.createPayment( new PaymentRequest(orderId, amount)); System.out.println("Payment status: " + response.status()); }}Click Run to see what this code prints.
Common Mistakes
- Forgetting @EnableFeignClients on the main application class, which silently skips Feign interface scanning.
- Using lb:// gateway routes without a discovery client on the classpath.
- Not handling FeignException in calling code, letting a downstream service failure bubble up unhandled.
- Putting business logic inside the gateway itself — a gateway should route and apply cross-cutting concerns, not contain domain logic.
Best Practices
- Keep gateway routes declarative and simple — push authentication/authorization logic into filters, not custom controllers.
- Group Feign client interfaces in a shared module if multiple services need to call the same downstream service.
- Combine Feign with a circuit breaker (e.g. Resilience4j) so one slow downstream service does not cascade failures.
- Version your internal APIs so gateway routes and Feign clients do not break silently on breaking changes.
Frequently Asked Questions
No. Zuul was Spring Cloud's older, servlet-based gateway. Spring Cloud Gateway is its reactive, Project Reactor-based successor and is now the recommended choice for new projects.
Yes. You can set a fixed url attribute on @FeignClient to call a specific host directly, bypassing service discovery entirely.
No, CORS still needs to be configured, typically at the gateway level so you only configure it once instead of on every downstream service.
Summary
spring-cloud-starter-gateway gives your system one routed entry point, and spring-cloud-starter-openfeign turns service-to-service HTTP calls into simple, typed interface methods.
- You understand what an API gateway does in a microservices system.
- You configured routes with spring-cloud-starter-gateway.
- You built a declarative REST client with @FeignClient.