reactive feign

Spring WebClient With Feign

Overview:

In this tutorial, I would like to show you how we can use Spring WebClient with Feign to make HTTP calls in reactive manner.

Spring WebClient with Feign:

Spring WebClient is a non-blocking reactive client to make HTTP requests. Feign is a library which helps us to create declarative REST clients easily with annotations and it provides better abstraction when we need to call an external service in Microservices Architecture.

In this tutorial, Lets see we could integrate these two.

Sample Application:

As the aim of this tutorial is to learn how we could integrate Spring WebClient with Feign and make HTTP requests, we need a service which exposes REST API for us to play with.

I am going to use json-server which exposes dummy REST-APIs.

  • This json file (db.json) will act like a DB.
{
   "movies":[
      {
         "id":1,
         "title":"Lord of the Rings - The Fellowship of the Ring",
         "year": 2001,
         "imdbRating": 8.8
      },
      {
         "id":2,
         "title":"Lord of the Rings - The Two Towers",
         "year": 2002,
         "imdbRating": 8.7
      },
      {
         "id":3,
         "title":"Lord of the Rings - The Return of the King",
         "year": 2003,
         "imdbRating": 8.9
      }            
   ]
}
  • In the same location, I also have a docker-compose file which creates a json-server container. I have done volume mapping for the json-server to read our db.json.
version: '3'
services:
  server:
    image: clue/json-server
    ports:
    - 3000:80
    volumes:
    - ${PWD}/db.json:/data/db.json
  • Once the docker container is up and running, we should be able to access below end points.
    • GET all the movies
http://localhost:3000/movies
    • GET a specific movie with id.
http://localhost:3000/movies/1
    • POST – Add a new movie into the DB
http://localhost:3000/movies
    • PUT – Update an existing movie by id
http://localhost:3000/movies/1
    • DELETE a movie by id
http://localhost:3000/movies/1

We have our movie-service ready. Lets create a Reactive Feign client to interact with this service.

Project Setup:

Lest create a Spring project with the following dependencies.

spring webclient with feign

We also need to include this dependency.

<dependency>
    <groupId>com.playtika.reactivefeign</groupId>
    <artifactId>feign-reactor-spring-cloud-starter</artifactId>
    <version>3.1.0</version>
    <type>pom</type>
</dependency>

DTO:

Lets create a DTO to represent the movie object.

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor(staticName = "create")
public class MovieDto {

    private Integer id;
    private String title;
    private Integer year;
    private Double imdbRating;

}

Spring WebClient with Feign – MovieClient Interface:

Lets perform simple CRUD operations with our MovieService. For that lets create an interface as shown below.

@ReactiveFeignClient(value = "movie-service", url = "${movie.service.url}")
public interface MovieClient {

    @GetMapping("movies")
    Flux<MovieDto> getAllMovies();

    @GetMapping("movies/{movieId}")
    Mono<MovieDto> getMovie(@PathVariable("movieId") Integer movieId);

    @PostMapping("movies")
    Mono<MovieDto> saveMovie(MovieDto movieDto);

    @PutMapping("movies/{movieId}")
    Mono<Void> updateMovie(@PathVariable("movieId") Integer movieId, MovieDto movieDto);

    @DeleteMapping("movies/{movieId}")
    Mono<Void> deleteMovie(@PathVariable("movieId") Integer movieId);

}
  • Spring auto-configuration scans for @ReactiveFeignClient annotation and creates the beans automatically for us.
  • movie.service.url can be injected via property file. In this case, I keep the value as shown below in the property file.
movie.service.url=http://localhost:3000
  • We have some abstract methods for performing various CRUD operations. This is enough and Feign will take care of the REST (!).
  • Lets NOT forget the @EnableReactiveFeignClients
@SpringBootApplication
@EnableReactiveFeignClients
public class WebClientFeignApplication {

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

}

Service:

  • Create a Service which implements CommandLineRunner.
    • We can autowire MovieClient directly as shown below.
@Service
public class FeignClientDemo implements CommandLineRunner {
    
    @Autowired
    private MovieClient movieClient;
    
    @Override
    public void run(String... args) throws Exception {
        
    }
}
  • Lets add methods one by one in the service class.
  • GET all movies
private Mono<Void> getAll(){
    return this.movieClient.getAllMovies()
            .doOnNext(System.out::println)
            .doFinally(s -> System.out.println("------------- GET All completed ------------------"))
            .then();
}
  • POST – add new movie
    • Here I keep the year as 1999. Later we will update it as 2001.
private Mono<Void> post(){
    MovieDto dto = MovieDto.create(
            5,
            "Harry Potter and the Sorcerer Stone",
            1999,
            7.6
    );
    return this.movieClient.saveMovie(dto)
            .doFinally(s -> System.out.println("------------- POST Movie completed ------------------"))
            .then();
}
  • PUT – update a movie
    • Here I correct the movie year from 1999 to 2001
private Mono<Void> put(){
    MovieDto dto = MovieDto.create(
            null,
            "Harry Potter and the Sorcerer Stone",
            2001,
            7.6
    );
    return this.movieClient.updateMovie(5, dto)
            .doOnNext(System.out::println)
            .doFinally(s -> System.out.println("------------- Movie updated ------------------"))
            .then();
}
  • GET a specific movie by id.
private Mono<Void> get(){
    return this.movieClient.getMovie(5)
            .doOnNext(System.out::println)
            .doFinally(s -> System.out.println("------------- GET Movie completed ------------------"))
            .then();
}
  • DELETE a movie by id.
private Mono<Void> delete(){
    return this.movieClient.deleteMovie(5)
            .doOnNext(System.out::println)
            .doFinally(s -> System.out.println("------------- Movie deleted ------------------"))
            .then();
}
  • Finally the run method invokes all these methods in the order as shown below.
@Override
public void run(String... args) throws Exception {
    Flux.concat(
            getAll(), // first get all movies
            post(),   // create a new movie id 5
            get(),    // get movie id 5 - check if it is present
            put(),    // update movie id 5
            get(),    // get movie id 5 - check if it is updated
            delete()  // delete movie id 5
    ).subscribe();
}

Spring WebClient with Feign – Demo:

When I start the application, the run method invokes all the methods and we are able to interact with the MovieService. We get the response as shown below.

MovieDto(id=1, title=Lord of the Rings - The Fellowship of the Ring, year=2001, imdbRating=8.8)
MovieDto(id=2, title=Lord of the Rings - The Two Towers, year=2002, imdbRating=8.7)
MovieDto(id=3, title=Lord of the Rings - The Return of the King, year=2003, imdbRating=8.9)
------------- GET All completed ------------------
------------- POST Movie completed ------------------
MovieDto(id=5, title=Harry Potter and the Sorcerer Stone, year=1999, imdbRating=7.6)
------------- GET Movie completed ------------------
------------- Movie updated ------------------
MovieDto(id=5, title=Harry Potter and the Sorcerer Stone, year=2001, imdbRating=7.6)
------------- GET Movie completed ------------------
------------- Movie deleted ------------------

Summary:

We were able to successfully demonstrate Spring WebClient with Feign to create a declarative reactive client to make HTTP requests without much effort.

The source code is available here.

Learn more about Spring WebFlux.

Happy coding 🙂

Share This:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.