When building a contact form backend in Spring Boot, it's good practice to separate your internal entity from incoming API requests. This can be done using a DTO (Data Transfer Object) โ in our case, a ContactRequest
. In this post, weโll walk through creating a JPA-backed contact service that uses a ContactRequest
DTO for request handling.
๐ฉ Step 1: Define the ContactRequest DTO
The DTO serves as the structure for incoming request payloads.
java
public class ContactRequest {
private String name;
private String email;
private String subject;
private String message;
// Getters and Setters
}
๐ฆ Step 2: Define the Contact Entity
This entity will be stored in the database.
java
import javax.persistence.*;
@Entity
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String subject;
private String message;
// Getters and Setters
}
๐พ Step 3: Create a JPA Repository
java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ContactRepository extends JpaRepository<Contact, Long> {
}
โ๏ธ Step 4: Define the Service Interface
Update the service interface to accept the ContactRequest
DTO for create and update operations.
java
import java.util.List;
public interface ContactService {
Contact saveContact(ContactRequest contactRequest);
List<Contact> getAllContacts();
Contact getContactById(Long id);
Contact updateContact(Long id, ContactRequest contactRequest);
void deleteContact(Long id);
}
๐ง Step 5: Implement the Service Logic
Hereโs the full implementation that maps between the DTO and entity:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ContactServiceImpl implements ContactService {
@Autowired
private ContactRepository contactRepository;
@Override
public Contact saveContact(ContactRequest contactRequest) {
Contact contact = new Contact();
contact.setName(contactRequest.getName());
contact.setEmail(contactRequest.getEmail());
contact.setSubject(contactRequest.getSubject());
contact.setMessage(contactRequest.getMessage());
return contactRepository.save(contact);
}
@Override
public List<Contact> getAllContacts() {
return contactRepository.findAll();
}
@Override
public Contact getContactById(Long id) {
return contactRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Contact not found with id: " + id));
}
@Override
public Contact updateContact(Long id, ContactRequest contactRequest) {
Contact contact = getContactById(id);
contact.setName(contactRequest.getName());
contact.setEmail(contactRequest.getEmail());
contact.setSubject(contactRequest.getSubject());
contact.setMessage(contactRequest.getMessage());
return contactRepository.save(contact);
}
@Override
public void deleteContact(Long id) {
Contact contact = getContactById(id);
contactRepository.delete(contact);
}
}
๐ Step 6: Add a REST Controller
This is how your controller can use the ContactRequest
for handling HTTP requests.
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/contacts")
public class ContactController {
@Autowired
private ContactService contactService;
@PostMapping
public ResponseEntity<Contact> createContact(@RequestBody ContactRequest contactRequest) {
return ResponseEntity.ok(contactService.saveContact(contactRequest));
}
@GetMapping
public ResponseEntity<List<Contact>> getAllContacts() {
return ResponseEntity.ok(contactService.getAllContacts());
}
@GetMapping("/{id}")
public ResponseEntity<Contact> getContactById(@PathVariable Long id) {
return ResponseEntity.ok(contactService.getContactById(id));
}
@PutMapping("/{id}")
public ResponseEntity<Contact> updateContact(@PathVariable Long id, @RequestBody ContactRequest contactRequest) {
return ResponseEntity.ok(contactService.updateContact(id, contactRequest));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteContact(@PathVariable Long id) {
contactService.deleteContact(id);
return ResponseEntity.noContent().build();
}
}
๐จ Step 7: Add Exception Handling (Optional but Recommended)
java
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
โ
Summary
By introducing a ContactRequest
DTO, you've added an abstraction layer between your REST API and your database, making your code more flexible and easier to maintain. This clean approach enables:
- Separation of concerns
- Future-proofing against changes in internal data structures
- Better validation and transformation support