Spring Cloud API Gateway With An Example

Reading Time: 6 minutes

What is an API? Why do we need it?

An API gateway is programming that sits in front of an API ( Application Programming Interface) and is the single-entry point for defined back-end APIs and microservices (which can be both internal and external). Sitting in front of APIs, the gateway acts as protection, administering security and scalability, and high availability. To put it simply, API Gateway takes all API requests from a customer, determines which services are demanded, and combines them into a unified, flawless experience for users. 

Advantages of API Gateway

  • It improves the security of the microservices as we limit the access of external calls to all our services.
  • The cross-cutting concerns like authentication, monitoring/metrics, and resiliency will be needed to be implemented only in the API Gateway as all our calls will be routed through it.
  • The client does not know about the internal architecture of our microservices system. The client will not be able to determine the location of the microservice instances.
  • Simplifies client interaction as he will need to access only a single service for all the requirements.

Spring Cloud Gateway Architecture?

Spring Cloud Gateway is API Gateway implementation by the Spring Cloud team on top of the Spring reactive ecosystem. It consists of the following building blocks-

Route: Route the basic building block of the gateway. It consists of an ID, destination URI Collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.

Predicate: This is similar to Java 8 Function Predicate. Using this functionality we can match HTTP requests, such as headers, URLs, cookies, or parameters.

Filter: These are instances of Spring Framework GatewayFilter. Using this we can modify the request or response as per the requirement.

Spring Cloud Gateway Architecture

Implementing Spring Cloud API Gateway

Using Spring Cloud Gateway we can create routes in either of the two ways –

  • Use java based configuration to programmatically create routes
  • Use property based configuration(i.e application.properties or application.yml) to create routes.

In this tutorial, we will be implementing Spring Cloud Gateway using property-based configurations.
We will be implementing Spring Cloud Gateway application which routes requests to two other microservices depending on the URL pattern.

Implement First MicroService:

The Maven project will be as follows-

The pom.xml will be as follows-

<?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>2.6.2</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com</groupId>
	<artifactId>first-microservice</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>first-microservice</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2021.0.0</spring-cloud.version>

	</properties>
	<dependencies>
		<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-web</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>

Define the application.yml as follows-

spring:
  application:
    name: first-microservice
server:
  port: 8081

  eureka:
   instance:
     hostname: localhost

Create a Controller class that exposes the GET REST service as follows-

package com.firstmicroservice.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/first")
public class FirstController {

    @GetMapping("/message")
    public String test() {
        return "Hello this is my first microservice";
    }
}

Create the main class with the @SpringBootApplication annotation.

package com.firstmicroservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FirstMicroserviceApplication {

	public static void main(String[] args) {
		SpringApplication.run(FirstMicroserviceApplication.class, args);
	}

}

Implement Second MicroService:

The Maven project will be as follows-

The pom.xml will be as follows-

<?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>2.6.2</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com</groupId>
	<artifactId>second-microservice</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>second-microservice</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2021.0.0</spring-cloud.version>

	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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>

Define the application.yml as follows-

spring:
  application:
    name: second-microservice
server:
  port: 8082

  eureka:
    instance:
      hostname: localhost

Create a Controller class that exposes the GET REST service as follows-

package com.secondmicroservice.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/second")
public class SecondController {

    @GetMapping("/message")
    public String test() {
        return "Hello this is my Second Service";
    }

}

Create the main class with the @SpringBootApplication annotation.

package com.secondmicroservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecondMicroserviceApplication {

	public static void main(String[] args) {
		SpringApplication.run(SecondMicroserviceApplication.class, args);
	}

}

Implement Eureka Server:

Eureka Server is an application that holds information  Above all,client-service applications. Every Microservice will register into the Eureka server and the Eureka server knows all the client applications running on each port and IP address. Eureka Server is also known as Discovery Server.

Building a Eureka Server

Eureka Server comes with the bundle of Spring Cloud. Therefore, we need to develop the Eureka server and run it on the default port 8761.

The Maven project will be as follows-

The pom.xml will be as follows-

<?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>2.6.2</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>eurekaserver</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>eurekaserver</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2021.0.0</spring-cloud.version>
	</properties>
	<dependencies>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-                  server</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>

Define the application.yml as follows-

 server:
  port: 8761
 eureka:
   client:
     register-with-eureka: false
     fetch-registry: false
   server:
     waitTimeInMsWhenSyncEmpty: 0
   instance:
     hostname: localhost

Create the main class with the @SpringBootApplication annotation.

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaserverApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaserverApplication.class, args);
	}

}

Implement Spring Cloud Gateway using property based config


The Maven project will be as follows-

The pom.xml will be as follows-

<?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>2.6.2</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com</groupId>
	<artifactId>springcloud-gateway</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springcloud-gateway</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
		<spring-cloud.version>2021.0.0</spring-cloud.version>
		<!--		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>-->

	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</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>

Define the application.yml as follows-

server:
  port: 8080

eureka:
  instance:
   hostname: localhost

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: first-microservice
          uri: http://localhost:8081/
          predicates:
            - Path=/first/**
        - id: second-microservice
          uri: http://localhost:8082/
          predicates:
            - Path=/second/**

Create the main class with the @SpringBootApplication annotation.

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class SpringcloudGatewayApplication {

	public static void main(String[] args) {

		SpringApplication.run(SpringcloudGatewayApplication.class, args);
	}

}

Start the three microservices we have developed-

  • Go to url – localhost:8080/first/message

Go to URL – localhost:8080/second/message

Conclusion:

API Gateway is an important design concept. With an increasing number of microservices, it becomes important to have a common pattern that can handle a lot of the common workload of these services, and API Gateway helps with that.

Written by 

I'm a Software Consultant at Knoldus Inc. I have done Post Graduation from Quantum University Roorkee. I have knowledge of various programming languages. I'm passionate about Java development and curious to learn Java Technologies. I'm always ready to learn new technologies and my hobbies are cricket and action movies.

1 thought on “Spring Cloud API Gateway With An Example13 min read

Comments are closed.