spring webflux webclient

Spring WebClient Example

Overview

In this article, I would like to show you how we could use Spring WebClient for making non-blocking HTTP requests for various CRUD operations.

Spring WebClient

Spring WebFlux is a non-blocking asynchronous reactive web framework. It includes WebClient (something like RestTemplate) which provides fluent API for making HTTP requests in an asynchronous and non-blocking way. It also supports streaming responses.

Let’s see how to make a following HTTP method calls using Spring WebClient.

  • GET
  • POST
  • PUT
  • DELETE

Sample Application

To learn Spring WebClient, Let’s have some server with REST endpoints. It is very simple if you have docker installed.

  • First create a simple JSON like this.
{
   "posts":[
      {
         "id":1,
         "title":"json-server",
         "author":"typicode"
      }
   ],
   "comments":[
      {
         "id":1,
         "body":"some comment",
         "postId":1
      }
   ]
}
  • Save the file as db.json
  • Run this docker command by mapping the file path with the container. It runs a json-server using the above json file as backend DB.
docker run -d -p 3000:80 -v ${PWD}/db.json:/data/db.json clue/json-server
  • now you should be able to access the endpoints to access the data from our db.json
// all posts 

http://localhost:3000/posts 

// all comments 

http://localhost:3000/comments 

// post 1 comments 

http://localhost:3000/posts/1/comments

Project Setup

Create a Spring project with the dependencies as shown here.

spring webclient with feign

DTO

Let’s create a simple model for Post.  I use lombok for getters and setters.

Post:

@Data
public class Post {

    private int id;
    private String title;
    private String author;

}

Comment:

@Data
public class Comment {
    
    private int id;
    private String body;
    private int postId;
    
}

Spring WebClient – Builder

Lets initialize few variables.

private static final String BASE_URL = "http://localhost:3000/";
private WebClient webClient;

I also create the new instance of immutable WebClient as shown here.

webClient = WebClient.builder()
        .baseUrl(BASE_URL)
        .build();

The WebClient has many other methods to customize which you could explore.

GET

The below method makes a GET request to the /posts endpoint which returns a Post[].

// http://localhost:3000/posts
webClient
        .get()
        .uri("/posts")
        .retrieve()
        .bodyToMono(Post[].class)
        .flatMapIterable(Arrays::asList)
        .subscribe(System.out::println);

In our case, we have only one post in the db. So I get the below output.

Post(id=1, title=json-server, author=typicode)

POST

Below sample code shows how we can post a DTO using WebClient.

// http://localhost:3000/posts

// create a new post object
Post post2 = new Post();
post2.setId(2);
post2.setAuthor("vinsguru");
post2.setTitle("project reactor");

//post via webclient
webClient
        .post()
        .uri("/posts")
        .bodyValue(post2)
        .retrieve()
        .bodyToMono(String.class)
        .subscribe(System.out::println);

Now accessing the below endpoint gives below JSON response.

// http://localhost:3000/posts

[
  {
    "id": 1,
    "title": "json-server",
    "author": "typicode"
  },
  {
    "id": 2,
    "title": "project reactor",
    "author": "vinsguru"
  }
]

PUT

To update/replace an existing record, we can use PUT request as shown here.

// http://localhost:3000/posts/2

// create a new post object
Post post2 = new Post();
post2.setId(2);
post2.setAuthor("vinsguru");
post2.setTitle("webclient");  // change the book title

//put via webclient
webClient
        .put()
        .uri("/posts/2")
        .bodyValue(post2)
        .retrieve()
        .bodyToMono(String.class)
        .subscribe(System.out::println);

Now I get below output when I access the endpoint.

// http://localhost:3000/posts

[
  {
    "id": 1,
    "title": "json-server",
    "author": "typicode"
  },
  {
    "id": 2,
    "title": "webclient",
    "author": "vinsguru"
  }
]

PATCH

To update just few fields instead of replacing the whole record.

Here we update the title to webclient-patch

// http://localhost:3000/posts/2

//patch via webclient
webClient
        .patch()
        .uri("/posts/2")
        .body(BodyInserters.fromFormData("title", "webclient-patch"))
        .retrieve()
        .bodyToMono(String.class)
        .subscribe(System.out::println);

DELETE

To delete a specific record from the DB.

// http://localhost:3000/posts/2

webClient
        .delete()
        .uri("/posts/2")
        .retrieve()
        .bodyToMono(String.class)
        .subscribe(System.out::println);

TimeOut

The reactive WebClient is more resilient. We can set the max wait time for a HTTP request to finish and default object to return when the request gets timed out.

webClient
        .get()
        .uri("/posts")
        .retrieve()
        .bodyToMono(Post[].class)
        .timeout(Duration.ofSeconds(1))  // max wait time 1 sec
        .flatMapIterable(Arrays::asList)
        .onErrorReturn(new Post(100, "default title", "default author"))
        .subscribe(System.out::println);

Summary

We were able to successfully demonstrate making HTTP requests using Spring WebClient. If you are using Feign, we can also use Feign with WebFlux. Check this out.

Learn more about Spring WebFlux.

Happy learning 🙂

References:

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.