Hello! Welcome back to our journey into backend development with Java and Spring Boot.
In our last lesson, you learned how to use ResponseEntity to gain full control over HTTP responses. We saw how to manually return status codes like 400 Bad Request if the client's input was invalid. While effective, writing if-else blocks for every possible validation rule can quickly clutter your controllers and mix validation logic with business logic.
Today, we'll address this by learning how to validate request data using Bean Validation annotations (@NotNull, @Size, @Valid). This is a powerful, declarative approach that keeps your code clean and follows industry best practices. We'll define validation rules directly on our data classes and let Spring handle the enforcement automatically.
1. The "Why" and "How" of Bean Validation
Instead of manually checking data, we can use a standard set of annotations to declare rules on the fields of our classes. For example, we can state that a user's name must not be blank or that their age must be at least 18.
Spring Boot integrates seamlessly with the Jakarta Bean Validation API (formerly Java Bean Validation or JSR-380). The most common implementation of this API is Hibernate Validator, which Spring Boot uses by default.
To get started, we need to add one dependency to our pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
This starter dependency pulls in everything we need. The relationship between these components can be visualized like this:

2. Applying Validation Annotations
Let's see how to use these annotations. We'll apply them to a Data Transfer Object (DTO), which is a class designed specifically to carry data between the client and our API.
Imagine we have a CreateUserRequest DTO.
public class CreateUserRequest {
private String username;
private String password;
private String email;
private int age;
// ...getters and setters...
}
Without validation, a client could send a request with a null username or an invalid email. Let's fix that.
The video below provides an excellent walkthrough of the most common validation annotations. We'll go through it in sections.
Spring Boot Validations Explained | @Valid, Annotations & Custom Validation
First, let's see how to add the dependency and use the most fundamental annotation, @NotNull. This video by 'The Curious Coder' also demonstrates the crucial @Valid annotation, which we'll discuss right after.
Watch the segments from 03:10 to 05:47. The first part shows adding the spring-boot-starter-validation dependency. The second part demonstrates how to apply @NotNull to a field and, importantly, how to add @Valid to the controller method parameter to trigger the validation.
As you saw, adding @NotNull to a field is simple. But the key is adding @Valid in your controller:
@PostMapping("/users")
public void createUser(@Valid @RequestBody CreateUserRequest request) {
// If validation fails, this code will not be reached.
// Spring throws an exception before the method body executes.
userService.createUser(request);
}
When Spring sees @Valid, it tells Hibernate Validator to inspect the CreateUserRequest object. If any validation rules are violated, Spring throws a MethodArgumentNotValidException and, by default, returns a 400 Bad Request response to the client.
Common Validation Annotations
The Bean Validation standard provides a rich set of annotations. Here is a quick reference table.
Spring Boot and Validation: A Complete Guide
This article provides a helpful table summarizing the most common validation annotations.
Review the table in section '3. Basic Validation Annotations'. This is a great reference for the annotations we are about to see in action.
Now, let's watch the same video to see a live demonstration of these annotations.
Spring Boot Validations Explained | @Valid, Annotations & Custom Validation
The following segments from 'The Curious Coder' video demonstrate a variety of powerful validation annotations. Pay attention to how each one works and the custom messages provided.
Watch these three segments: @NotEmpty vs. @NotBlank (13:01 - 15:36): Understand the subtle but important difference. @Size, @Min, @Max (15:49 - 19:19): Learn how to constrain string length and numeric ranges. @Email (23:09 - 25:56): See how to enforce a valid email format.
To summarize the key distinctions:
@NotNull: The value cannot benull.@NotEmpty: Used for strings or collections. Cannot benulland its size/length cannot be zero. An empty string""is not allowed, but a string with only spaces" "is.@NotBlank: Only for strings. Cannot benulland the trimmed length must be greater than zero. This is often the most useful for string fields likeusername.
3. Creating User-Friendly Error Responses
When validation fails, Spring's default error response is functional but very verbose and not ideal for a production API. In the last lesson, you learned to create custom responses with ResponseEntity. We can combine that knowledge with an exception handler to format validation errors cleanly.
This is a preview of a topic we'll cover in-depth in the next lesson (Global Exception Handling), but it's essential for making validation practical.
Spring Boot Validations Explained | @Valid, Annotations & Custom Validation
This part of the video is crucial. It shows you how to intercept the MethodArgumentNotValidException and create a structured, clean JSON response that tells the client exactly which fields are invalid and why.
Watch carefully from 06:07 to 12:31. The video introduces @RestControllerAdvice and @ExceptionHandler to create a global handler. Notice how it loops through the validation errors to build a Map of field names to error messages. This is a very common and professional pattern.
By implementing an exception handler like the one in the video, you transform a messy default error into a clear and predictable response:
Default Error (Simplified):
{
"timestamp": "...",
"status": 400,
"error": "Bad Request",
"errors": [
{ "field": "password", "defaultMessage": "size must be between 8 and 20" },
{ "field": "email", "defaultMessage": "must be a well-formed email address" }
],
"path": "/users"
}
Custom Error Response (from video):
{
"password": "size must be between 8 and 20",
"email": "must be a well-formed email address"
}
This custom response is much easier for a client application to parse and display to the user.
Test your understanding!
You are adding a tags field to a CreateProductRequest DTO. The requirements are:
- The list of tags itself cannot be null.
- The list can be empty, but if it contains tags, no tag can be a blank string.
- A product can have at most 5 tags.
How would you annotate the tags field?
// private List<String> tags;
Show answer
Here is the correct combination of annotations:
@NotNull(message = "Tags list cannot be null")
@Size(max = 5, message = "A product can have at most 5 tags")
private List<@NotBlank(message = "Tag cannot be blank") String> tags;
@NotNullapplies to theListobject itself.@Sizealso applies to theList, checking its size.@NotBlankis placed inside the generic type (List<@NotBlank ...>) to apply the validation to eachStringelement within the list. This is a powerful feature of Java 8+ and Bean Validation.
4. Validating Nested Objects and Collections
What if your DTO contains another object? For example, a CreateUserRequest might contain an Address object.
public class CreateUserRequest {
@NotBlank
private String username;
// How do we validate the fields inside the Address object?
private Address address;
}
public class Address {
@NotBlank
private String street;
@NotBlank
private String city;
}
To make Spring validate the nested address object, you must mark it with @Valid.
public class CreateUserRequest {
@NotBlank
private String username;
@Valid // Tells Spring to validate the fields inside the Address object
@NotNull // Ensures the Address object itself isn't null
private Address address;
}
This "cascading" validation is essential for complex request bodies. The same principle applies to validating elements inside a collection, as you saw in the "Test your understanding!" exercise.
The video below also provides a clear example of validating a collection.
Spring Boot Validations Explained | @Valid, Annotations & Custom Validation
Let's reinforce the concept of validating collections. This final video segment demonstrates applying @Size to a List and highlights a common pitfall: forgetting to also use @NotNull.
Watch from 32:01 to 34:13. Notice how @Size(min=1) doesn't protect against a null list, which is why combining it with @NotNull is important for robust validation.
Conclusion
Congratulations! You've learned how to implement declarative, automated validation in your Spring Boot application. This approach significantly improves code quality by separating validation rules from your business logic, making your controllers cleaner and your API more robust.
Key Takeaways:
- Add the
spring-boot-starter-validationdependency to enable Bean Validation. - Apply validation annotations like
@NotBlank,@Size,@Min,@Email, etc., directly to the fields of your DTOs. - Use the
@Validannotation on@RequestBodyparameters in your controller methods to trigger validation. - Validation failures result in a
MethodArgumentNotValidException, which Spring automatically maps to a400 Bad RequestHTTP status. - You can create a custom, user-friendly error response by using
@RestControllerAdviceand an@ExceptionHandlerforMethodArgumentNotValidException. - To validate nested objects or elements within a collection, use the
@Validannotation on the field.
What's Next?
In this lesson, we had a sneak peek at creating a custom exception handler specifically for validation errors. However, a real-world application needs to handle many other types of errors: resources not being found, business rule violations, security issues, and more.
In the next lesson, we will dive deep into this topic and learn how to implement global exception handling using @ControllerAdvice and @ExceptionHandler. You'll learn how to build a centralized, consistent error-handling strategy for your entire API, ensuring that clients always receive predictable and useful error responses, no matter what goes wrong.