Zuul API Gateway Tutorial

In this tutorial you will learn how to create and run your own Zuul API Gateway and how to register with it more than one Spring Boot Microservice.

To be able to follow this tutorial you need to know how to create simple RESTful Web Services with Spring Boot. If you need to review that, please have a look at this tutorial that teaches how to create a very simple RESTful Web Services with Spring Boot.

Here is what we will do in this tutorial:

  1. Start up Eureka Discovery Server,
  2. Start up a simple Users Microservice. This can be any other Microservice you have,
  3. Create, configure and start up a new Zuul API Gateway,
  4. Learn how to send HTTP Requests to a Microservice via Zuul API Gateway.

Start Up Eureka Discovery Server

Since this tutorial is about Zuul API Gateway I will not go in details on how to create and configure your Eureka Discovery Server and will simply show you my source code. But if you are interested to learn in more details how to create a Eureka Discovery Service, please follow this short tutorial I have created earlier: Create and run a very simple Eureka Discovery Server.

Below are the details of my Eureka Discovery Server which I have created for this tutorial.

Application Java Class

package com.appsdeveloperblog.photoapp.discovery.PhotoAppDiscoveryService;

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

@SpringBootApplication
@EnableEurekaServer
public class PhotoAppDiscoveryServiceApplication {
 public static void main(String[] args) {
  SpringApplication.run(PhotoAppDiscoveryServiceApplication.class, args);
 }
}

Eureka Server application.properties File

server.port=8010
spring.application.name=PhotoAppApi-eureka-server
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false

Start up Eureka Discovery Service and open its URL in the browser window. In the application.properties file above I have configured port 8010 to be used for my Eureka server. So the URL I need to open is:

http://localhost:8010

Start Up Spring Boot Microservice

The Netflix Zuul API Gateway we are going to create in this tutorial will route HTTP Requests sent to a Microservice registered with Eureka. You might already have one or more Spring Boot Microservices created but if you do not have, please follow this tutorial on how to make your Microservice registered with Eureka Discovery Server.

Below are the details of the Spring Boot Microservice I have created for this tutorial.

Application Java class

package com.appsdeveloperblog.photoapp.api.users;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PhotoAppApiApplication {
 public static void main(String[] args) {
  SpringApplication.run(PhotoAppApiApplication.class, args);
 }
}

Users Microservice application.properties File

spring.devtools.restart.enabled = false
eureka.client.serviceUrl.defaultZone = http://localhost:8010/eureka
server.port=0
spring.application.name=users-ws

Users Microservice Rest Controller Class

package com.appsdeveloperblog.photoapp.api.users.io.controllers;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class UsersController {
    
    @GetMapping("/status/check")
    public String status() {
        return "Working";
    }
}

Start up your Spring Boot Microservice and if you have configured it to register with Eureka Discovery Server as I have done it in the example of application.properties file above, then your Microservice should register with Eureka successfully. Refresh Eureka page and it should now have your Spring Boot Microservice listed.

Starting up a Second Microservice

Let’s assume I have two Microservices I want to register with my Eureka Discovery service. All I need to do is to create a new Microservice and follow the steps above to update its application.properties file to point to correct Eureka url and to have a unique application name. Here is an example of application.properties file of my second Microservice which is called albums-ws.

Albums Web Service application.properties file

spring.devtools.restart.enabled = true
eureka.client.serviceUrl.defaultZone = http://localhost:8010/eureka
server.port=0
spring.application.name=albums-ws

As you can see the application.properties file of my second Microservice looks very much the same except it has a different application name: albums-ws. After starting up a second Microservice, if I refresh the Eureka Registry page I should see two Microservices registered.

Zuul API Gateway

It is time to create a Zuul API Gateway. To create a new API Gateway with Netflix Zuul we will first need to create a very simple Spring Boot Web Service. I almost always create Spring Boot Web Services using the Spring Initializr project page. Go ahead and create a new Spring Boot Web Service project or follow this step by step video tutorial to create a new Spring Boot Web Service.

Zuul API Gateway Dependencies

Once you have your very simple Spring Boot Web project created, open its POM.xml file and add the following details:

<dependenyManagement>

    <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>

Gateway Starter and Eureka Client 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.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-zuul</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>

The <repositories> element

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

The <properties> element

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Greenwich.RC2</spring-cloud.version>
</properties>

The 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 http://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.1.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.appsdeveloperblog.photoapp.api.gateway</groupId>
    <artifactId>ZuulApiGateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ZuulApiGateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.RC2</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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</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>
    
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

Zuul API Gateway Application.properties File

server.port=8011
spring.application.name=zuul
eureka.client.serviceUrl.defaultZone = http://localhost:8010/eureka
eureka.client.fetchRegistry=true
eureka.client.registerWithEureka=true

Zuul API Gateway Main Application Java File

Next, we need to update the main Application Java file of our Zuul Spring Boot app. Locate in you Zuul project the Java which contains public static void main(String[] args) and add above the class the following two annotations:

  • @EnableEurekaClient  – Zuul API Gateway also needs to register with Eureka Discovery Service
  • @EnableZuulProxy – this annotation will make our Spring Boot App behave as a Zuul API Gateway.
package com.appsdeveloperblog.photoapp.api.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ApiGatewayApplication {

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

}

And this is it. Although this is a very basic setup of Zuul API Gateway it should work well and you should be able to communicate with your RESTful Web Services via the Gateway.  You can also start up more instance of same RESTful Web Services and Zuul will also load balance HTTP Requests between them. More on how to start up more of the same Microservices and see how load balancer works I will share with you in my following tutorial.

Sending HTTP Request

Now when you have your RESTful Web Services registered with Eureka Discovery Server and you have your Zuul API Gateway started, you should be able to send HTTP requests to any of the two web services registered and receive expected HTTP response.

The URL to Users Microservice 

Let’s have a look at how the request URL to our Microservice should look like. Below are the URLs to the two Microservices we have started up in this tutorial and have made them available via the Zuul API Gateway. So the HTTP requests you send to your RESTful Web Services should be now sent via the gateway.

  • http://localhost:8011/users-ws/users/status/check
  • http://localhost:8011/albums-ws/users/status/check

where:

  • http://localhost – is the protocol and the domain name of your Zuul API Gateway,
  • 8011 – port number you have specified in the application.properties file of your Zuul API Gateway,
  • /users-ws and /albums-ws – are the application names you have assigned to your Microservices in the application.properties file. Have a look at the property called “spring.application.name” in the above mentioned examples of application.properties file of each of the RESTful Web Services,
  • /users and the /albums  – is the Request Mapping you have in you @RestController class. If you look at the example of Rest Controller call of Users Microservice above, it has a @RequestMapping(“/users”) annotation,
  • /status/check – is the method level Request Mapping in the Rest Controller class. For example, @GetMapping(“/status/check”).

I hope this tutorial was helpful. If you are interested in learning more about Spring Cloud, have a look at the below list of video courses that teach Spring Cloud. Hopefully one of them will be very helpful to you.