As part of our REST with Spring Series In this post, we will discuss data conversion for Spring REST API.
1. Introduction
While designing REST API, we always require converting internal entities to more generic external DTO sent back to the client as response data. There are multiple ways to convert internal entities of a Spring application and the external DTOs.In this post, we will cover different options to do this.
2. Data Conversion API’s
There are a number of APIs for converting application specific object to generic DTO (Data Transfer Object). Below is the list of popular APIs
For this post, we are using Model Mapper API but you are free to use any of the above API or even your own custom data mapper API.
3. Model Mapper Setup
We are using Maven for our post, let’s do initial setup before start using ModelMapper API. Maven user adds the modelmapper library as a dependency.
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>1.1.0</version>
</dependency>
We need a ModelMapper instance to start working on the mapping. For the Spring configuration, we will define ModelMapper bean
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
//For non Spring application, we can create instance by using new keyword
ModelMapper modelMapper = new ModelMapper();
4. Source and Target Entities
We are taking an example of the Order object passed to the client API after conversion. Let’s check how order entity look like
class Order {
private String orderNumber;
private double orderAmount;
private double tax;
private Customer customer;
private Address shippingAddress;
//getter and setter
}
class Customer {
private String userId;
private String firstName;
private String lastName;
private String email;
//getter and setter
}
class Address {
private String addressLine1;
private String street;
private String city;
private String postalCode;
//getter and setter
}
Here is the external DTO which we like to send to the client API as part of the API response.
public class OrderDto {
public String orderNumber;
private double orderAmount;
private double tax
private Customer customer;
private Address shippingAddress;
//getters and setters
}
5. The Facade Layer
I will be using a Facade layer in this example to simplify the service layer. Facade layer will be responsible for the following operation
- Call service API and to fetch the required data.
- Data conversion.
- Any additional data messaging.
This is how our OrderFacade class look like.
public class OrderFacade {
@Autowired
private OrderService orderService;
@Autowired
private ModelMapper modelMapper;
public OrderDto getOrderById(String id){
return convertToOrderDto(orderService.getOrderById(id));
}
private OrderDto convertToOrderDto(Order order) {
OrderDto orderDto = modelMapper.map(order, OrderDto.class);
return orderDto;
}
}
Data conversion from Order entity to OrderDto is done by the convertToOrderDto method
private OrderDto convertToOrderDto(Order order) {
OrderDto orderDto = modelMapper.map(order, OrderDto.class);
return orderDto;
}
6. The Service Layer
The service layer will call OrderRepository to get an order by order number. It will return Order data back to the OrderFacade.
@Service("orderService")
public class OrderService {
public Order getOrderByCode(String id){
return orderRepository.findById(id) ;
}
}
[pullquote align=”normal”]We are not handling exception cases. Read our article on Error Handling for REST with Spring for detail. [/pullquote]
7. The Controller
The Controller layer is the main entry point for the REST client. The controller is responsible for the following action items
- Expose endpoint to the REST client.
- Convert incoming request in to application specific data (content negotiation).
- Call underlying system API to get desired results.
- Send back the response to the customer by converting data into customer acceptable format (based on “Accept” header).
This is how our OrderController looks like.
@RestController
public class OrderController {
@Autowired
private OrderFacade orderFacade;
@GetMapping(value = "/orders/{id}")
public @ResponseBody ResponseEntity<OrderDto> getOrder(@PathVariable("id") String id){
OrderDto order = orderFacade.getOrderById(id);
return new ResponseEntity<<(order, HttpStatus.OK);
}
}
Let’s reiterate some of the main points while working on the conversion.
- The data conversion logic is quick and simple.
- We need least or no code to map source entity into destination DTO.
7. Running Application
The last step is to run our application and test it. We are building our application on Spring Boot. To test an application
- Use a web browser.
- Use REST client like Postman, Advance REST client etc.
On running the application and hitting URL like http://localhost:8080/orders/1, here is the output
{
"orderNumber":"1234",
"orderAmount":200.34,
"tax":10.12,
"customer":{
"userId":"[email protected]",
"firstName":"Umesh",
"lastName":"Awasthi",
"email":"[email protected]"
},
"shippingAddress":{
"addressLine1":"Adress line 1",
"street":"street 1",
"city":"San Jose",
"postalCode":"95111"
}
}
8. Unit Testing
If you like, we can write a simple unit test to make sure our data conversion is working as expected.
import com.javadevjournal.rest.data.Address;
import com.javadevjournal.rest.data.CustomerModel;
import com.javadevjournal.rest.data.Order;
import com.javadevjournal.rest.dto.OrderDto;
import org.junit.Test;
import org.modelmapper.ModelMapper;
import static org.junit.Assert.assertEquals;
public class OrderDataConversionUnitTest {
private ModelMapper mapper = new ModelMapper();
@Test
public void convert_Order_To_OrderDto(){
Order order = new Order();
order.setOrderNumber("1234");
order.setOrderAmount(200.34)
order.setTax(10.12);
//customer
CustomerModel customer= new CustomerModel();
customer.setUserId("[email protected]");
customer.setFirstName("Umesh");
customer.setLastName("Awasthi");
customer.setEmail("[email protected]");
order.setCustomer(customer);
Address shippingAddress = new Address();
shippingAddress.setAddressLine1("Adress line 1");
shippingAddress.setStreet("street 1");
shippingAddress.setCity("San Jose");
shippingAddress.setPostalCode("95111");
order.setShippingAddress(shippingAddress);
OrderDto orderDTO = mapper.map(order, OrderDto.class);
assertEquals(order.getCustomer().getFirstName(), orderDTO.getCustomer().getFirstName());
assertEquals(order.getCustomer().getLastName(), orderDTO.getCustomer().getLastName());
assertEquals(order.getOrderNumber(), orderDTO.getOrderNumber());
}
}
Summary
In this article, we covered the data conversion for Spring REST API. We discussed the conversion from Entity to DTO in Spring REST API. We can use the same technique to convert DTO back to the entity object. Source code for this article is available on the GitHub
Why is Order and OrderDTO have same properties than what’s the point of creating OrderDTO?
Send Order class as it is .
This all comes down to what to want to expose to the frontend or to the client. This example is simple one but the real life entities are more complex and carries a lot of information and you don’t want to send all the data to the customer (more over they might not need all that data).
For a REST API, it’s all about the contract between the REST client and REST API and to have 2 different object makes your life easy and it’s also more flexible to change the underlying objects without even changing the view.
Can you please also cover how to save data using DTO object?
Hi jasper, I can surely cover it, but on a high level it’s not more than converting the DTO to an entity and saving it. The underlying JPA operation for REST API is not different than what we will do for normal web application (Though their can be design difference for REST application).
Let me know if you have any specific questions and we can surely talk about those.
Is there an example on github?
I have updated the article with the link to the examples. Here is the link for your reference
https://github.com/umeshawasthi/javadevjournal/tree/master/Spring-Boot/rest-api-data-conversion