Rest Template with Spring Boot
Let’s interact with services and other libraries
Hi guys! Today’s article is all about calling other services / endpoints from our Spring Boot application. There are often usages of this scenario in a real application.
- Sometimes we get requirements to communicate between services to fetch some data and return data as an enriched response — Ex: To include the department details of the employee(1:M relationship), we need to call department service
- Sometimes the flow of events is not completed if we do not communicate between services — Ex: To create an order, we need to call from order service to payment service
For this Spring Boot provides an awesome class! It’s Rest Template. There are 2️⃣ main usages of Rest Template class. They are:
- Inter service communication — we can give exact API endpoints OR if we use Eureka cloud discovery, we can use service name with API endpoints.
- Call to 3rd party libraries — Such as APIs developed by some platforms where we need an API Key to access data
Let’s see its usages…
Add Rest Template
This class is available under org.springframework.web dependency which is known as spring-boot-starter-web.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
You should add this dependency anyway to create a REST API or Micro service.
Setup Rest Template Bean
At the root level or any config related folder, we can create a separate config for this. I have name it as WebConfig. This class should be annotated with “@Configuration”. So we can return the created bean inside a method annotated with “@Bean”.
@Configuration
public class WebConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofMillis(3000))
.setReadTimeout(Duration.ofMillis(3000))
.build();
}
}
Use Rest Template
We have to inject the Rest template bean into the place where we use it. Normally business logic is in service layer. So, we have to auto-wire the bean into service. Then singleton object of Rest Template with be used everywhere in the application.
@Autowired
private RestTemplate restTemplate;
API Methods in Rest Template
There are several API methods overloaded to perform a GET API call using Rest Template.
// Without parameters
<T> T getForObject(URI url, Class<T> responseType)// Parameter object list as var args
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables)// Parameter object list as a map
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
The same goes with POST API calls also. We have the same set of methods. Only the parameters are different.
<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
Request body can be any kind of object here. We can create a DTO / Pojo for that and pass it to the method.
ForObject vs ForEntity
You may have seen that there are two types of methods which are named slightly different. As an example I can say: getForObject and getForEntity…What is the difference? Let’s see method definitions.
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables)<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
Look at the return types!
- First method returns an object which is having the type of response mapped class. We can access that object’s data if we need them for next logic. That’s all.
- But in the second method, we get the object wrapped inside a ResponseEntity. There we have more details. Since it is a ResponseEntity, we can access API details like status / status code / headers. This is better when we need to check status and execute the next logic. Ex: — if and only if payment request gives status code 200, send the order response back. We need to call getBody() method to get the exact response data from of the wrapper.
Call Third Party Library 💥
We can perform all HTTP methods such as GET/POST/PUT/DELETE, using Rest Template. So, I will show them using some already available APIs. Here I will use the famous fake REST API knows as JSON Placeholder to demonstrate the API methods available in Rest Template.
Website: https://jsonplaceholder.typicode.com
There are several fake resource endpoints like /posts, /todos and etc. I’m using Todo resource endpoints.
Simple GET Call
Let’s say we need to fetch a Todo from this API. Then we have to see the response format and create a Pojo to map it in Java code. This is a MUST for any API call. We need a mapping class! This is the JSON format in our case.
Then our class will be like this.
@Builder
@Data
class Todo {
String userId;
String id;
String title;
boolean completed;
}
Now we can simply use getForObject method to fetch data. First parameter is URL we are going to call. It can be another service / 3rd party library. Second parameter is the mapping class for the response.
- Single resource — Todo
Todo result = restTemplate
.getForObject("https://jsonplaceholder.typicode.com/todos/1", Todo.class);
- Multiple resources — Todo[]
Todo[] todos = restTemplate
.getForObject("https://jsonplaceholder.typicode.com/todos", Todo[].class);
Note:
If you use getForEntity method, code block will be like this.
ResponseEntity<Todo[]> todos = restTemplate
.getForEntity("https://jsonplaceholder.typicode.com/todos", Todo[].class);
Todo[] todoList = todos.getBody();
System.out.println(todos.getStatusCode().name()); // OK
System.out.println(todos.getStatusCodeValue()); // 200
GET Call with Query Parameters
As a third parameter we have to pass parameter Map here, to the API method. It is a simple key value pair.
- Ex: I need to fetch the todos which are already completed. Then we can pass a query parameter like this.
API URL: https://jsonplaceholder.typicode.com/todos?completed=true
Calling this method in Rest Template: It has map of parameters…
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
Code snippet:
Map<String, String> map = new HashMap<>();
map.put("completed", "true");
Todo[] todosCompleted = restTemplate
.getForObject("https://jsonplaceholder.typicode.com/todos?completed={completed}", Todo[].class, map);
We have another overloaded method 😎 to use. Let’s take this example. We need to find all completed todos of use ID 1.
https://jsonplaceholder.typicode.com/todos?completed=true&userId=1
Calling this method in Rest Template: It has object var args for parameters…We can pass any number of parameters individually. 💪
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
Code snippet:
String completedStatus = "true";
String userId = "1";Todo[] todosCompletedOfUser = restTemplate
.getForObject("https://jsonplaceholder.typicode.com/todos?completed={completedStatus}&userId={userId}", Todo[].class, completedStatus, userId);
GET Call with Path Parameters
Ex: I need to fetch a specific todo with the ID. Then we have to pass a path parameter like this.
API URL: https://jsonplaceholder.typicode.com/todos/1
Map<String, String> map = new HashMap<>(); Todo resource
map.put("id", "1");
Todo todo = restTemplate
.getForObject("https://jsonplaceholder.typicode.com/todos/{id}", Todo.class, map);
System.out.println(todo);
Simple HTTP POST Call
We have to create a Todo resource and pass as the 2nd parameter in postForObject method.
Todo todoObject = Todo.builder().id("123").userId("345").title("Learn Rest Template").completed(false).build();Todo todoCreated = restTemplate
.postForObject("https://jsonplaceholder.typicode.com/todos", todoObject, Todo.class);
System.out.println(todoCreated);
Note:
If you use postForEntity method, code block will be like this.
ResponseEntity<Todo> todoResponse = restTemplate
.postForEntity("https://jsonplaceholder.typicode.com/todos", todoObject, Todo.class);
Todo todoInserted = todoResponse.getBody();
System.out.println(todoResponse.getStatusCode().name()); // CREATED
System.out.println(todoResponse.getStatusCodeValue()); // 201
System.out.println(todoInserted);
NOTE: 📓
- If you have only 1 parameter to be sent, you can use the method that takes Object var args as the argument. No need of using a Map due to space constraints in the program. If there are multiple parameters, you can use any method according to your desire.
Todo[] result = restTemplate
.getForObject("https://jsonplaceholder.typicode.com/todos?api_key={apiKey}", Todo[].class, apiKey);
- You can use parameterized methods for POST call also. These methods are available for both GET and POST calls.
Call to External APIs with API Keys 💥
If you need to connect with a totally external API which needs API keys needed for accessing data, we can do it simply.
Ex: https://www.themoviedb.org/
- This platform provides movie related data. We can simply signup and get the key. Then the API URLs will follow the below format.
https://api.themoviedb.org/3/movie/550?api_key=123456
So, we have to define the API Key inside application.properties. Then we can inject it using “@Value”.
Config properties:
moviedb.api.key=123456
Service layer:
@Value("${moviedb.api.key}")
String apiKey;
Let’s create the parameters map and pass to get the Movies list.
Map<String, String> parameters = new HashMap<>();
parameters.put("apiKey", apiKey);Movie todosCompleted = restTemplate
.getForObject("https://api.themoviedb.org/3/movie/550?api_key={apiKey}", Movie.class, parameters);
Call to another Micro Service 💥
Let;s imagine we have to process an order in our system. In this case, Order is not completed until we make the payment. So, we have 2️⃣ micro services called order-service and payment-service . After creating the order using incoming request data, we have to send the buyer data like name and amount to the payment service, to initiate the transaction.
Let’s mimic the case with code…
Order order = Order.builder().id("123").userId("james").total(2000).build();PaymentRequest paymentRequest = PaymentRequest.builder().userId(order.getUserId()).amount(order.getTotal()).build();// Call payment service
Payment payment = restTemplate
.postForObject("https://payment-service/pay", paymentRequest, Payment.class);
So, in this API call, we can provide Base URL in different ways.
- Exact URL mapped by DevOps
- Exact public IP:port combination
- Eureka Service Discovery service name defined in configs
That’s all guys! 😎 This is a detailed explanation of using Rest Template for inter service / library communication. So, try this and let me know if there is anything to know.
Bye bye!