Data Conversion for Spring REST API

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

  1. Dozer
  2. ModelMapper
  3. MapStruct
  4. Orika
  5. Selma

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

6 thoughts on “Data Conversion for Spring REST API”

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

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

Comments are closed.