In this tutorial, we’ll build an edge server using Spring Cloud Gateway.
Dependency
To create an edge server using Spring Cloud Gateway, following is the dependency:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
We’ll also need the spring-cloud-starter-netflix-eureka-client
dependency as our gateway server is going to register with Eureka server. Following is our complete pom.xml
:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.learnitweb</groupId> <artifactId>gatewayserver</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gatewayserver</name> <description>Edge server for microservices</description> <url/> <licenses> <license/> </licenses> <developers> <developer/> </developers> <scm> <connection/> <developerConnection/> <tag/> <url/> </scm> <properties> <java.version>17</java.version> <spring-cloud.version>2024.0.0</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Configuration
Following is the complete configuration file (application.yml) of the gateway server:
server: port: 8072 spring: application: name: "gatewayserver" cloud: gateway: discovery: locator: enabled: true eureka: client: fetchRegistry: true registerWithEureka: true service-url: defaultZone: http://localhost:8070/eureka/ instance: hostname: localhost prefer-ip-address: true management: endpoints: web: exposure: include: "*" # Use "health,info" in production endpoint: gateway: access: unrestricted info: env: enabled: true
- spring.application.name: “gatewayserver”
- This sets the name of the Spring Boot application to
"gatewayserver"
. - The application name is often used for identification in logs, monitoring systems, and service discovery when working with tools like Eureka or Consul.
- This sets the name of the Spring Boot application to
- spring.cloud.gateway.discovery.locator.enabled: true
- Purpose: This enables the gateway to dynamically route requests to services registered in a service discovery mechanism (e.g., Eureka, Consul).
- The Discovery Locator functionality allows the API Gateway to automatically map routes to services without explicitly defining routes in the configuration file.
- When enabled:
- It discovers all the services registered in the discovery service.
- It creates default routes for each of the discovered services.
How it Works
- Service Discovery Integration:
- The gateway connects to a service discovery tool (like Eureka).
- All microservices registered with the discovery server are visible to the gateway.
- Dynamic Routing:
- The gateway generates routes automatically based on the registered service names.
- For example:
- If a service named
inventory-service
is registered, the gateway would map a route like:http://<gateway-host>:<port>/inventory-service/**
This forwards requests to theinventory-service
.
- Simplifies Configuration:
- No need to manually configure routes for each service.
- This is especially useful in environments with dynamic scaling or frequent service updates.
Demo of Edge Server with default routing config
After the gateway server is set. Start the Eureka server, Gateway server and other microservices. Once everything is started, the Eureka server dashboard looks like the following:

All routes on the gateway server can be seen at the following endpoint:
http://localhost:8072/actuator/gateway/routes
[ { "predicate": "Paths: [/PRODUCTSERVICE/**], match trailing slash: true", "metadata": { "jmx.port": "50265", "management.port": "8071" }, "route_id": "ReactiveCompositeDiscoveryClient_PRODUCTSERVICE", "filters": [ "[[RewritePath /PRODUCTSERVICE/?(?\u003Cremaining\u003E.*) = '/${remaining}'], order = 1]" ], "uri": "lb://PRODUCTSERVICE", "order": 0 }, { "predicate": "Paths: [/GATEWAYSERVER/**], match trailing slash: true", "metadata": { "jmx.port": "50233", "management.port": "8072" }, "route_id": "ReactiveCompositeDiscoveryClient_GATEWAYSERVER", "filters": [ "[[RewritePath /GATEWAYSERVER/?(?\u003Cremaining\u003E.*) = '/${remaining}'], order = 1]" ], "uri": "lb://GATEWAYSERVER", "order": 0 }, { "predicate": "Paths: [/INVENTORYSERVICE/**], match trailing slash: true", "metadata": { "jmx.port": "50283", "management.port": "8073" }, "route_id": "ReactiveCompositeDiscoveryClient_INVENTORYSERVICE", "filters": [ "[[RewritePath /INVENTORYSERVICE/?(?\u003Cremaining\u003E.*) = '/${remaining}'], order = 1]" ], "uri": "lb://INVENTORYSERVICE", "order": 0 } ]
Now, we’ll access the inventory service endpoint using the gateway server. Following is the endpoint:
localhost:8072/PRODUCTSERVICE/api/inventory/car
Note that here, 8072
is the port of the gateway server. We are calling the endpoint of PRODUCTSERVICE
.
@RestController @RequestMapping("/api") public class InventoryController { @Autowired private InventoryFeignClient inventoryFeignClient; @GetMapping("/inventory/{name}") public ResponseEntity<?> getProductInventory(@PathVariable String name) { ResponseEntity<InventoryDto> result = inventoryFeignClient.getProductInventory(name); return result; } }
PRODUCTSERVICE calls the INVENTORYSERVICE using the Feign client.
Following is the sample output:
{ "productName": "some car", "quantityRemaining": 100 }
Change in Gateway server to accept service name in lowercase
Earlier we used the capital letters while using the service:
localhost:8072/PRODUCTSERVICE/api/inventory/car
Bu that should not be the case. We should allow service names to be in lowercase. To do that, add lowerCaseServiceId: true
to the application.yml of gateway server.
spring: application: name: "gatewayserver" cloud: gateway: discovery: locator: enabled: true lowerCaseServiceId: true