When developing web applications or REST APIs with Spring Boot, validating request data is an unavoidable concern. For example, when registering a user, you need to check that the name and email address are not empty and that they follow the correct format.
That’s where the @Valid annotation comes in handy. This article walks through the basics of @Valid, from its core role to how to define actual validation rules in practice.
What is @Valid?
@Valid is an annotation that triggers validation processing conforming to the Jakarta Bean Validation (formerly JSR 303/380) specification. In Spring Boot, by adding spring-boot-starter-validation, you can easily apply validation to controller method arguments and Java objects.
However, @Valid itself only serves as a trigger — it marks an object as a validation target. What gets validated and how is defined by the constraint annotations placed on each field.
Why Is It Commonly Used in API Development?
In Spring Boot REST APIs, it is common to receive request data from clients as JSON. At that point, the received data is mapped to a POJO (Plain Old Java Object), and by annotating it with @Valid, Spring automatically executes validation.
@PostMapping("/users")
public ResponseEntity<String> createUser(@RequestBody @Valid UserRequest userRequest) {
return ResponseEntity.ok("User created");
}
This allows you to design the system so that when a request is invalid, Spring throws a MethodArgumentNotValidException and returns an appropriate error response.
Validation Rules Are Defined with Annotations
Once @Valid enables validation on an object, you can apply constraint annotations to each field to fine-tune what gets validated.
public class UserRequest {
@NotBlank(message = "名前は必須です")
@Size(min = 2, max = 20, message = "名前は2〜20文字で入力してください")
private String name;
@Email(message = "メールアドレスの形式が正しくありません")
private String email;
}
As shown here, you can also combine multiple constraints on a single field.
Commonly Used Constraint Annotations
The following constraint annotations are available for validation:
| Annotation | Description |
|---|---|
@NotNull | Value must not be null |
@NotBlank | Empty strings or whitespace-only strings are not allowed |
@NotEmpty | Empty collections, arrays, or strings are not allowed |
@Size(min=, max=) | Constrains the length or number of elements |
@Email | Validates email format |
@Pattern(regexp=) | Validates against a regular expression |
@Min, @Max | Constrains the range of a numeric value |
@Positive, @Negative | Validates that a number is positive or negative |
@Past, @Future | Validates that a date is in the past or future |
You can also define custom error messages by specifying the message attribute on these annotations.
Validating Nested Objects
@Valid can also apply validation recursively to nested objects.
public class OrderRequest {
@Valid
private Address address;
}
In this case, the validation rules defined inside the Address class are also applied.
Manual Validation in the Service Layer
You can also explicitly execute validation in classes other than controllers (e.g., the Service layer) using a Validator.
@Service
public class UserService {
private final Validator validator;
public UserService(Validator validator) {
this.validator = validator;
}
public void register(UserRequest request) {
Set<ConstraintViolation<UserRequest>> violations = validator.validate(request);
if (!violations.isEmpty()) {
throw new IllegalArgumentException("Validation failed: " + violations);
}
// Registration logic
}
}
Differences Between @Valid and @Validated
Spring’s own @Validated annotation can be used similarly to @Valid. The key difference is that it enables more flexible control, such as group validation and method-level validation.
@Component
@Validated
public class UserValidator {
public void validate(@Valid UserRequest request) {
// Validated automatically
}
}
DTO Design Rules for Production Use
From a maintenance perspective, aligning your DTO validation around the following rules makes things easier to maintain:
- Separate API input DTOs from DB entities
- Limit the responsibility of each field and avoid excessive validation
- Design error messages with both the user-facing and log-facing audiences in mind
- Use
@NotNulland@NotBlankaccording to their purpose (prefer@NotBlankfor strings)
Rather than “adding constraints to everything just in case,” defining only what is necessary as an input contract makes the code more resilient to change.
Standardizing Error Responses
If you use @Valid, it is important to standardize the response format for MethodArgumentNotValidException up front.
Inconsistent formats across endpoints increase the implementation cost on the frontend side.
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidation(MethodArgumentNotValidException ex) {
var errors = ex.getBindingResult().getFieldErrors().stream()
.map(error -> Map.of(
"field", error.getField(),
"message", error.getDefaultMessage()
))
.toList();
return ResponseEntity.badRequest().body(Map.of(
"code", "VALIDATION_ERROR",
"errors", errors
));
}
}
Fixing the response keys (code, errors, field, message) like this stabilizes how consumers handle the response.
Common Pitfalls
Validation Passes but Business Requirements Are Not Met
Annotation-based constraints are strong for “format checks,” but “business rule” validation is a separate concern.
For example, checking whether an email address already exists requires a database query, so that additional check belongs in the Service layer.
Forgetting to Add @Valid to Nested DTOs
Even if you add @Valid to the parent DTO, recursive validation will not occur unless @Valid is also on the nested DTO field.
When using nested structures, make sure to check the annotations on both the parent and child.
Hardcoding Validation Messages
If there is any possibility of supporting multiple languages in the future, centralizing messages in messages.properties is an effective design.
@NotBlank(message = "{validation.name.required}")
private String name;
Summary
Using the @Valid annotation, you can implement input validation in Spring Boot concisely and flexibly. The actual validation rules are defined by placing constraint annotations on the target fields. This improves API robustness while also providing users with clear, meaningful error messages.
Since you can use Validator to perform validation within any component — not just controllers — you can adapt your approach to match your business logic as needed.
Give the @Valid annotation a try and start building more robust applications!