Spring BootREST APIsbeginner
Updated:

Request Validation in Spring Boot with Bean Validation

5 min read

How to reject bad input at the edge of your API using Bean Validation annotations, the @Valid trigger, and clean field-level error responses.

TL;DR – Quick Answer

Request validation in Spring Boot uses the Bean Validation standard to check incoming data before your code runs. You annotate the fields of a request object with constraints like @NotBlank and @Size, then add @Valid to the controller parameter to trigger the checks. If validation fails, Spring throws MethodArgumentNotValidException, which you turn into a clean 400 response listing the invalid fields.

On This Page

The safest assumption when building an API is that clients will send you garbage: empty names, negative quantities, malformed emails, missing fields. Request validation is the wall you build at the edge of your application so that garbage is rejected with a clear message before it ever reaches your business logic. Spring Boot makes this declarative — you describe the rules with annotations and let the framework enforce them.

Validate at the boundary, not deep inside

The principle behind good validation is to reject bad input as early as possible. If you let an invalid order object travel through three service layers before a database constraint finally rejects it, the resulting error is confusing and expensive. Catch it at the controller, where you can return a precise 400 Bad Request naming the exact field that is wrong.

Spring Boot implements the Bean Validation standard (Jakarta Validation) through Hibernate Validator. You annotate a request object once, and the same rules apply everywhere that object is received. This pairs naturally with the endpoints you build in the REST API with Spring Boot tutorial.

Add the validation starter

Validation lives in its own starter, so add it to your build first:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

This brings in Hibernate Validator and the constraint annotations. In older Spring Boot versions validation shipped inside the web starter, but it is now separate — a detail worth remembering, because a missing starter is the most common reason constraints appear to be ignored. If you are unsure what a starter contains, the Spring Boot starters tutorial explains how to inspect one.

Annotate the request object

Put constraints directly on the fields of the object that represents the incoming request:

import jakarta.validation.constraints.*;

public class CreateUserRequest {

    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 50, message = "Name must be 2 to 50 characters")
    private String name;

    @NotBlank(message = "Email is required")
    @Email(message = "Email must be valid")
    private String email;

    @Min(value = 18, message = "Age must be at least 18")
    @Max(value = 120, message = "Age looks invalid")
    private int age;

    // getters and setters
}

Each annotation is a rule with a custom message. The messages matter — they become the text your API returns, so write them for the client, not for yourself. The most useful common constraints are @NotNull, @NotBlank, @Size, @Min, @Max, @Email and @Pattern for regex checks.

Pro tip: Know the difference between the three "not empty" annotations. @NotNull only rejects null. @NotEmpty rejects null and empty. @NotBlank rejects null, empty, and whitespace-only strings. For text fields a user types, @NotBlank is almost always what you actually want.

Trigger validation with @Valid

Annotations on the object do nothing until you tell Spring to check them. Add @Valid to the controller parameter:

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public String create(@Valid @RequestBody CreateUserRequest request) {
        // reached only if validation passed
        return "Created user " + request.getName();
    }
}

Now when a request arrives, Spring validates it before entering the method body. If every rule passes, your code runs normally. If any rule fails, Spring throws MethodArgumentNotValidException and your method is never called — the invalid data never touches your logic. This is the enforcement point that makes the whole system trustworthy.

Common mistake: Adding constraint annotations to the fields but forgetting @Valid on the parameter. Without @Valid, Spring binds the object but never checks the rules, so invalid data sails straight through. If your constraints seem to do nothing, this is the first place to look.

Return clean validation errors

By default Spring Boot returns a verbose response when validation fails. For a polished API, capture the exception in a global handler and format it yourself, exactly as you would for any error in REST exception handling:

import org.springframework.http.*;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import java.util.*;

@RestControllerAdvice
public class ValidationHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handle(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(err ->
            errors.put(err.getField(), err.getDefaultMessage()));
        return ResponseEntity.badRequest().body(errors);
    }
}

Now a bad request for the user endpoint returns a tidy 400 like {"email":"Email must be valid","age":"Age must be at least 18"}. The client learns every problem at once, field by field, instead of fixing one error only to hit the next. That single response shape is what makes an API pleasant to integrate against.

Validating nested objects

If your request contains another object — say an Address inside CreateUserRequest — its constraints are checked only if you cascade validation with @Valid on the field:

public class CreateUserRequest {
    @NotBlank
    private String name;

    @Valid            // cascade validation into Address
    @NotNull
    private Address address;
}

Without the inner @Valid, Spring validates the top-level name but ignores every constraint inside Address. Cascading is opt-in by design, so you control exactly how deep validation goes.

Validating path variables and query parameters

Not all input arrives in a request body. To validate a @PathVariable or @RequestParam directly, put the constraint on the parameter and mark the controller class with @Validated:

import org.springframework.validation.annotation.Validated;

@RestController
@RequestMapping("/api/products")
@Validated
public class ProductController {

    @GetMapping("/{id}")
    public String get(@PathVariable @Min(1) Long id) {
        return "Product " + id;
    }
}

Here @Validated at the class level enables method-parameter validation, and @Min(1) rejects a zero or negative id with a ConstraintViolationException. This is the key distinction between the two trigger annotations: @Valid cascades into an object's fields, while @Validated on the class turns on validation of loose method parameters. You handle the resulting ConstraintViolationException in your advice just as you handle body validation, keeping every error response uniform.

Writing a custom constraint

The built-in annotations cover most needs, but domain rules sometimes require your own. A custom constraint is an annotation paired with a validator class:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PincodeValidator.class)
public @interface ValidPincode {
    String message() default "Must be a 6-digit Indian pincode";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
public class PincodeValidator
        implements ConstraintValidator<ValidPincode, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext ctx) {
        return value != null && value.matches("\\d{6}");
    }
}

Now @ValidPincode works exactly like a built-in constraint anywhere you place it. Custom constraints keep validation declarative and reusable — the rule lives in one place and reads clearly at the point of use, rather than being buried as an if check inside a service. This is the pattern to reach for when the same business rule appears on more than one field or request across your Spring Boot application.

Interview relevance

Validation questions test whether you build defensively. Be ready to name the common constraint annotations, explain the crucial difference between @NotNull, @NotEmpty and @NotBlank, and describe the two-step mechanism: constraints on the object plus @Valid on the parameter to trigger them. A strong answer connects validation to error handling — a failed check throws MethodArgumentNotValidException, which you turn into a clean 400. These come up together in the Spring Boot REST API interview questions, and getting the @Valid trigger and nested cascading right is what separates a real answer from a memorised list. Building this habit early is one reason learners in the Java Full Stack program ship APIs that survive real traffic.

Frequently Asked Questions

What is the difference between @Valid and @Validated?
@Valid is the standard Bean Validation annotation and is what you normally put on a controller method's @RequestBody parameter. @Validated is a Spring-specific annotation that adds validation groups and enables method-level validation on beans. For validating request bodies, @Valid is the usual choice.
Which dependency do I need for validation in Spring Boot?
You need spring-boot-starter-validation, which brings in the Hibernate Validator implementation and the Bean Validation API. In older Spring Boot versions validation was included in the web starter, but now it is a separate starter you must add explicitly for the constraint annotations to work.
What is the difference between @NotNull, @NotEmpty and @NotBlank?
@NotNull only checks that the value is not null. @NotEmpty additionally checks that a string or collection has at least one element. @NotBlank goes further for strings and requires at least one non-whitespace character, so a string of only spaces fails @NotBlank but passes @NotEmpty.
How do I validate nested objects?
Place @Valid on the nested field inside the parent object. Without @Valid on that field, Spring validates only the top-level fields and skips the constraints inside the nested object. Adding @Valid cascades validation into the nested structure so its constraints are checked too.
What happens when validation fails in Spring Boot?
Spring throws MethodArgumentNotValidException before your controller method body runs. If you do nothing, Spring Boot returns a default 400 response. Best practice is to handle that exception in a @RestControllerAdvice and return a clean error body listing each invalid field and its message.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 15 July 2026 LinkedIn
Chat with us