• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Contact Us

All about code

  • Java
    • Spring
      • Spring Boot
      • Spring Cloud
    • Java Miscellaneous Tips
    • Java Design Patterns
  • Data Structures and Algorithms
    • Algorithms
    • Data Structures
  • Thoughts and Stories
  • AI

How to Implement a Gateway with Spring Cloud

door portal gateway fantasy worlds 5094596

Introduction

A micro-service system could have a high number of components with complex interactions. It is important to reduce this complexity at least from the standpoint of the clients interacting with the system. A gateway hides the micro-services from the external world. It represents a unique entrance and implements common cross requirements. In this article, you will learn how to configure a gateway component for a Spring Boot application, using the Spring Cloud Gateway package.

Spring Cloud Gateway

Spring Cloud provides a gateway implementation by the Spring Cloud Gateway project. It is based on Spring Boot, Spring WebFlux, and Reactor. Since it is based on Spring WebFlux, it must run on a Netty environment, not a usual servlet container.

The main function of a gateway is to route requests from external clients to the micro-services. Its main components are:

  • Route: this is the basic entity. It is configured with an ID, a destination URI, one or more predicates, and filters
  • Predicate: this is based on a Java function predicate. It represents a condition that must be matched on head or request parameters
  • Filter: it allows you to change the request or the response

We can identify the following sequence of events:

  • A client makes a call through the gateway
  • The gateway decides if the request matches a configured route
  • If there is a match the request is sent to a gateway web handler
  • The web handler sends the request to a chain of filters that can execute logic relative to the request or the response, and operate changes on them
  • The target service is executed

Spring Cloud Gateway Dependencies

To implement our Spring Boot application as a gateway we must first provide the spring-cloud-starter-gateway dependency after having defined the release train as in the example below:

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>2023.0.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>
        ...
	</dependencies>

Spring Cloud Gateway Configuration

We can configure our gateway component using the application.yaml file. We can specify fully expanded arguments or shortcuts to define predicates and filters. In the first case, we define a name and an args field. The args field can contain one or more key-value pairs:

spring:
  cloud:
    gateway:
      routes:
      - id: route-example
        uri: https://example.com
        predicates:
        - name: PredicateExample
          args:
            name: predicateName
            regexp: predicateRegexpValue

In the example above, we define a route with an ID value of “route-example”, a destination URI “https://example.com”, and a predicate with two args, “name” and “regexp”.

With the shortcut mode, we write the predicate name followed by the “=” character, and then a list of names and values separated by commas. We can rewrite the previous example by the following:

spring:
  cloud:
    gateway:
      routes:
      - id: route-example
        uri: https://example.com

        predicates:
        - Cookie=predicateName,predicateRegexpValue

A specific factory class implements each predicate and filter type. There are several built-in predicate and filter factories available. The Cookie predicate shown above is an example. We will list some of them in the following sub-sections.

Predicate Built-In Factories

  • The After Predicate Factory matches requests that happen after a specific time
    • After=2007-12-03T10:15:30+01:00 Europe/Paris
  • The Before Predicate Factory matches requests that happen before a specific time
    • Before=2007-12-03T10:15:30+01:00 Europe/Paris
  • The Method Route Predicate Factory specifies the HTTP method types to match
    • Method=GET,POST

Filter Built-In Factories

  • The AddRequestHeader GatewayFilter Factory allows the addition of an HTTP header to the request by its name and value
    • AddRequestHeader=X-Request-Foo, Bar
  • The AddRequestParameter GatewayFilter Factory allows the addition of a parameter to the request by its name and value
    • AddRequestParameter=foo, bar
  • The AddResponseHeader GatewayFilter Factory allows the addition of an HTTP header to the request by its name and value
    • AddResponseHeader=X-Response-Foo, Bar

To implement a custom predicate or filter factory, we have to provide an implementation of a specific factory interface. The following sections show how.

Custom Predicate Factories

To create a custom predicate factory we can extend the AbstractRoutePredicateFactory, an abstract implementation of the RoutePredicateFactory interface. In the example below we define an inner static class Configuration, to pass its properties to the apply method and compare them to the request.

@Component
public class CustomPredicateFactory extends AbstractRoutePredicateFactory<CustomPredicateFactory.Configuration> {

    public CustomPredicateFactory() {
        super(Configuration.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Configurationconfig) {
        return exchange -> {
            ServerHttpRequest request = exchange.getRequest();
            //compare request with config properties
            return matches(config, request);
        };
    }

    private boolean matches(Configuration config, ServerHttpRequest request) {
        //implement matching logic
        return false;
    }

    public static class Configuration{
        //implement custom configuration properties
    }
}

Custom Filter Factories

To create a custom filter factory, we can extend the AbstractGatewayFilterFactory, an abstract implementation of the GatewayFilterFactory interface. In the examples below, you can see a filter factory that modifies the request and another one that changes the response, using the properties passed by a Configuration object.

@Component
public class PreCustomFilterFactory extends AbstractGatewayFilterFactory<PreCustomFilterFactory.Configuration> {

    public PreCustomFilterFactory() {
        super(Configuration.class);
    }

    @Override
    public GatewayFilter apply(Configuration config) {
        return (exchange, chain) -> {
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            //use builder to modify the request
            return chain.filter(exchange.mutate().request(builder.build()).build());
        };
    }

    public static class Configuration {
        //implement the configuration properties
    }

}

@Component
public class PostCustomFilterFactory extends AbstractGatewayFilterFactory<PostCustomFilterFactory.Configuration> {

    public PostCustomFilterFactory() {
        super(Configuration.class);
    }

    @Override
    public GatewayFilter apply(Configuration config) {
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                    //Change the response
            }));
        };
    }

    public static class Configuration {
        //implement the configuration properties
    }

}

Spring Cloud Gateway Example

We will show a practical and simple example to see how the gateway works in a real scenario. You will find a link to the source code at the end of the article. The example is based on the following stack:

  • Spring Boot: 3.2.1
  • Spring Cloud: 2023.0.0
  • Java 17

We consider a minimal micro-service system that implements a library, with only two services: a book service and an author service. The book service calls the author service to retrieve an author’s information by passing an authorName parameter. The implementation of the two applications is based on an embedded in-memory H2 database and uses JPA ORM to map and query the Book and Author tables. From the standpoint of this demonstration, the most important part is the /getAuthor REST endpoint exposed by a BookController class of the Book service:

@RestController
@RequestMapping("/library")
public class BookController {

    Logger logger = LoggerFactory.getLogger(BookService.class);

    @Autowired
    private BookService bookService;

    @GetMapping(value = "/getAuthor", params = { "authorName" })
    public Optional<Author> getAuthor(@RequestParam("authorName") String authorName) {
        return bookService.getAuthor(authorName);
    }
}

The two applications register themselves in an Eureka discovery server and are configured as discovery clients. The final component is the gateway. The gateway should not register itself with the service discovery server. This is because it is called only by external clients, not internal microservices. On the other hand, it can be configured as a discovery client to fetch the other services automatically and implement a more dynamic routing. We don’t do it here, though, to keep things simple.

In this example, we want to show two things:

  • See how the routing mechanism by the predicate value works
  • Show how to modify the request by a filter, adding a header

The gateway’s configuration is the following:

spring:
   application:
      name: gateway-service
   cloud:
      gateway:
         routes:
            - id: add_request_header_route
              uri: http://localhost:8082
              predicates:
                 - Path=/library/**
              filters:
                 - AddRequestHeader=X-Request-red, red

We have defined a route with id “add_request_header_route”, and a URI value of “http://localhost:8082”, the base URI of the book service. We then have a Path predicate with a “/library/**” value. Every call starting with “http://localhost:8080/library” will be matched and routed toward the book’s service with URIs starting with “http://localhost:8082/library”.

Running the Example

To run the example, you can start each component by executing “mvn spring-boot:run” command from the component’s base directory. You can then test it by executing the “http://localhost:8080/library/getAuthor?authorName=Goethe” URI. The result will be a JSON value containing info about the author. If you check the browser developer tools, you will also find that an X-Request-red header has been added to the request with a value of “red”.

Conclusion

Implementing a gateway with the Spring Cloud Gateway package is the natural choice in the Spring Boot framework. It reduces the complexity of a microservice environment by placing a single facade component in front of it. It also gives you great flexibility in implementing cross-cutting concerns like authentication, authorization, aggregate logging, tracing, and monitoring.

You can find the source code of the example in this article on GitHub.

Primary Sidebar

Social

  • Facebook
  • LinkedIn
  • Twitter

Archives

  • May 2026
  • April 2026
  • March 2026
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • September 2023
  • May 2023
  • March 2023
  • January 2023
  • November 2022
  • September 2022
  • August 2022

Recent Posts

  • Better Avoid Spring Boot Lazy-initialization @PostConstruct Trap
  • How To Handle Large Datasets in Spring Boot
  • How to Log HTTP Incoming Requests in Spring Boot
  • How to Reliably Implement Post-Commit Actions in Spring
  • Brief and Easy Explanation of HATEOAS

TAGS

Algorithms Apache POI Backtracking Date Structures Dynamic Programming engineering Graphs Greedy Horror Stories HSSF Java Java New Features Java Principles Java tips real life Sorting Spring Boot Spring Cloud Strings Trees

Footer

Privacy Policy Cookie Policy Terms and Conditions