How to implement DTOs in Spring boot
1.Why
The DTO pattern is powerful to make sure that you are returning safe data to your end user
2.Simple Explanation
The ideia is that we use the DTOS to transfer data from the client to our services and from our services to the client, keeping it more concise and making sure that the user does not have access to data eh souldn’t.
We define the data that we want to receive from the client in the CreateClientDTO and the data we will send the client in the ResposeClientDTO
In this example i’ll be using Lombock just to automatically create some boiler plate for us.
Client model
@Getter
@Setter
@NoArgsConstructor
@Entity
public class Client {
public Client(String name, String email, String password) {
this.name = Objects.requireNonNull(name);
this.email= Objects.requireNonNull(email);
this.password = this.encrypt(password);
}
private String encrypt(String password) {
return password;
}
@Id()
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
}
For correctly creating our Client using a DTO we must have a constructor with no id provided, as this will be automatically created by @GeneratedValue method, you will understand more about it later on.
CreationDTO
@Getter
@AllArgsConstructor
public class CreateClientDTO {
private String name;
private String email;
private String password;
}
Here in the CreateClientDTO we will have only the variables that we need the client to send us for the user to be correctly created, note that the id is not here.
**
responseDTO
@Getter
@AllArgsConstructor
public class ResponseClientDTO {
private Long id;
private String name;
private String email;
}
And here in the response DTO we will have only the data that we want to send back to our client, note that the password is not here.
Mapper
@Component
public class Mapper {
public ResponseClientDTO toDto(Client client) {
Long id = client.getId();
String name = client.getName();
String email = client.getEmail();
return new ResponseClientDTO(id, name, email);
}
public Client toClient(CreateClientDTO clientDTO) {
return new Client(clientDTO.getName(), clientDTO.getEmail(), clientDTO.getPassword());
}
}
The mapper is the class we are gonna use to transform the data that comes from our service to the data that we want to send the user, and vice-versa.
ClientController
@RestController
@RequestMapping("/client")
public class ClientController {
private final ClientRepository clientRepository;
private final Mapper mapper;
@Autowired
public ClientController(ClientRepository clientRepository, Mapper mapper) {
this.clientRepository = clientRepository;
this.mapper = mapper;
}
@GetMapping("/{clientId}")
public ResponseEntity<ResponseClientDTO> getClient(@PathVariable Long clientId) {
Optional<Client> client = clientRepository.findById(clientId);
return client.map(c -> ResponseEntity.ok().body(mapper.toDto(c)))
.orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<ResponseClientDTO> createClient(@RequestBody CreateClientDTO clientDTO) {
Client client = mapper.toClient(clientDTO);
Client createdClient = clientRepository.save(client);
return ResponseEntity.ok().body(mapper.toDto(createdClient));
}
}
Here in our ClientController, in our @GetMapping("/{clientId}"), we are getting the user from the repository using the findById, that our client provided, then mapping it to return as a ResponseEntity parsing it to the DTO model using our mapper.
In the @PostMapping we do both, transform the data our client sent as a DTO through the body into a Client using the mapper, then save it using the clientRepository and transform it into the DTO again before sending the response to our client.
Oldest comments (0)