Validation "without human involvement" is like having a conscientious robot that checks your mail before you read it: it tosses spam and flags important messages. Similarly, the @Valid annotation lets you automate checking incoming data in your controllers and services.
Main aspects of @Valid
- It's part of the Bean Validation API specification.
- Automatically checks whether incoming data matches the validation annotations declared in DTO (Data Transfer Object) or other objects.
- Helps simplify and centralize data validation, keeping your code clean and readable.
Simple example: you accept JSON as the body of a POST request and you need the "email" field to be required and match an email format. Instead of doing manual checks (for example, with if), @Valid combined with a few validation annotations handles it all automatically.
How to use @Valid in controllers?
Step 1. Declaring the DTO
Let's start by creating a DTO class. DTO (Data Transfer Object) is an object used to transfer data, for example from client to server.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class UserDTO {
@NotNull(message = "Username cannot be empty.")
@Size(min = 2, max = 30, message = "Username must be between 2 and 30 characters.")
private String username;
@NotNull(message = "Email is required.")
@Email(message = "Invalid email format.")
private String email;
public UserDTO() {}
public UserDTO(String username, String email) {
this.username = username;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Here we use:
@NotNull— to make sure the field is not empty.@Size— to limit the string length.@Email— to check that the email is valid.
Field covered! Moving to the next step.
Step 2. Creating the REST controller
Now use the @Valid annotation in the controller to automatically validate incoming data.
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDTO) {
// If the data is valid, execution reaches here.
return new ResponseEntity<>("User successfully created", HttpStatus.CREATED);
}
}
@RestControllerindicates that this class is a REST controller.@RequestBody— converts JSON into aUserDTOobject.@Valid— triggers validation that checks the fields ofUserDTO.
If incoming data is valid, the request body is successfully converted into an object and the server returns HTTP 201 (Created). If validation fails, Spring will automatically throw MethodArgumentNotValidException.
Step 3. Adding custom messages
Each validation annotation in UserDTO contains a message parameter that specifies the error text. For example, if the email field does not match the format, the client will get the message "Invalid email format.".
Example application using @Valid
Let's put everything into a small app.
Main application code
DTO:
public class UserDTO {
@NotNull(message = "Username cannot be empty.")
@Size(min = 2, max = 30, message = "Username must be between 2 and 30 characters.")
private String username;
@NotNull(message = "Email is required.")
@Email(message = "Invalid email format.")
private String email;
// Getters, setters and constructor
}
Controller:
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDTO) {
return new ResponseEntity<>("User successfully created", HttpStatus.CREATED);
}
}
application.properties:
server.port=8080
What if the data is not valid?
When incorrect data is sent, Spring will automatically return HTTP 400 (Bad Request) with an error message in JSON format:
{
"timestamp": "2023-10-01T12:34:56.789+00:00",
"status": 400,
"error": "Bad Request",
"message": "Invalid email format.",
"path": "/api/users"
}
Common mistakes
- Missing Bean Validation dependency in
pom.xml. Your project might be missing the required dependency. Make sure yourpom.xmlincludes the Bean Validation module:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> - Request body is not converted to DTO. If you forget to add
@RequestBody, the JSON simply won't be recognized. Always remember that annotation. - Wrong import for the
@Validannotation. Make sure you're usingjakarta.validation.Valid(orjavax.validation.Validin older versions), not something from third-party libraries. - Exception handling forgotten. Spring can throw
MethodArgumentNotValidException, which, if not handled, will lead to ugly and confusing messages for the user.
Practical use
Validation via @Valid is widely used in real projects for:
- Automatic checking of data coming from clients.
- Preventing errors early, before data reaches business logic or the database.
- Improving code readability and maintainability.
In interviews you might be asked to describe how you implement validation in Spring, or better yet — to write a simple REST operation with @Valid. Knowing this tool will give you a solid advantage.
GO TO FULL VERSION