Introduction
Imagine booking a movie ticket online.
If you accidentally enter -2 tickets or leave the city name empty, the system immediately shows an error—before proceeding further.
That early check is called validation.
In REST APIs, query parameters are often used for filtering, searching, and pagination. If these values are invalid and you don’t validate them properly, your API may behave unpredictably or even crash.
In this blog, you’ll learn how to validate query parameters in Spring Boot, explained in simple language, with end-to-end Java 21 examples, complete with cURL requests and responses.
Core Concepts
What Does “Validating Query Parameters” Mean?
Validating query parameters means:
- Ensuring required parameters are present
- Checking values are within acceptable limits
- Rejecting bad input early with clear error messages
Example:
/products?page=-1&size=0
Without validation → ❌ broken logic
With validation → ✅ clean 400 BAD_REQUEST
Key Annotations Used
Spring Boot uses Jakarta Bean Validation:
| Annotation | Purpose |
|---|---|
@NotBlank |
String must not be empty |
@Min / @Max
|
Numeric range validation |
@Pattern |
Regex-based validation |
@Validated |
Enables validation for query params |
⚠️ Important:
For query parameters, @Validated is mandatory.
Without it, validation will not trigger.
Why Validate Query Parameters?
✅ Prevent invalid requests
✅ Improve API reliability
✅ Protect business logic
✅ Provide clear client feedback
✅ Reduce production bugs
Code Examples (End-to-End)
✅ Example 1: Validate Pagination Query Parameters
Use Case
Fetch products with pagination.
Step 1: REST Controller
package com.example.demo.controller;
import jakarta.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
@Validated // 🔴 REQUIRED for query param validation
public class ProductController {
/**
* Example:
* /products?page=1&size=10
*/
@GetMapping
public String getProducts(
@RequestParam
@Min(value = 0, message = "Page must be 0 or greater")
int page,
@RequestParam
@Min(value = 1, message = "Size must be at least 1")
int size) {
return "Fetching products [page=" + page + ", size=" + size + "]";
}
}
import java.time.OffsetDateTime;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import jakarta.validation.ConstraintViolationException;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<Map<String, Object>> handleMissingParams(MissingServletRequestParameterException ex) {
return ResponseEntity.badRequest()
.body(Map.of("status", 400, "error", "Bad Request", "message", ex.getParameterName() + " is required"));
}
// 🔹 Validation failure (@NotBlank, @Pattern, etc.)
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, Object>> handleConstraintViolation(ConstraintViolationException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("timestamp", OffsetDateTime.now(), "status",
400, "error", "Bad Request", "message", ex.getMessage(), "path", "/users"));
}
}
Step 2: Test with cURL
✅ Valid Request
curl "http://localhost:8080/products?page=1&size=10"
✅ Response
Fetching products [page=1, size=10]
❌ Invalid Request
curl "http://localhost:8080/products?page=-1&size=0"
❌ Response
{
"status": 400,
"error": "Bad Request",
"message": "Page must be 0 or greater"
}
✔ Invalid input rejected early
✔ Clean HTTP status
✔ No controller clutter
✅ Example 2: Validate String Query Parameters
Use Case
Search users by role and status.
Step 1: Controller with String Validation
package com.example.demo.controller;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
/**
* Example:
* /users?role=ADMIN&status=ACTIVE
*/
@GetMapping
public String getUsers(
@RequestParam
@NotBlank(message = "Role is required")
@Pattern(
regexp = "ADMIN|USER",
message = "Role must be ADMIN or USER"
)
String role,
@RequestParam(required = false)
@Pattern(
regexp = "ACTIVE|INACTIVE",
message = "Status must be ACTIVE or INACTIVE"
)
String status) {
return "Fetching users [role=" + role + ", status=" + status + "]";
}
}
import java.time.OffsetDateTime;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import jakarta.validation.ConstraintViolationException;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<Map<String, Object>> handleMissingParams(MissingServletRequestParameterException ex) {
return ResponseEntity.badRequest()
.body(Map.of("status", 400, "error", "Bad Request", "message", ex.getParameterName() + " is required"));
}
// 🔹 Validation failure (@NotBlank, @Pattern, etc.)
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, Object>> handleConstraintViolation(ConstraintViolationException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("timestamp", OffsetDateTime.now(), "status",
400, "error", "Bad Request", "message", ex.getMessage(), "path", "/users"));
}
}
Step 2: Test with cURL
✅ Valid Request
curl "http://localhost:8080/users?role=ADMIN&status=ACTIVE"
✅ Response
Fetching users [role=ADMIN, status=ACTIVE]
❌ Invalid Role
curl "http://localhost:8080/users?role=GUEST"
❌ Response
{
"status": 400,
"error": "Bad Request",
"message": "Role must be ADMIN or USER"
}
❌ Missing Required Parameter
curl "http://localhost:8080/users"
❌ Response
{
"status": 400,
"error": "Bad Request",
"message": "Role is required"
}
Best Practices
Always use
@Validatedon controllers
Without it, query parameter validation won’t work.Validate at the boundary
Reject bad input before it reaches business logic.Use meaningful validation messages
Clients should understand what went wrong.Keep query params simple
Complex validation may indicate a request body is better.Do not use validation annotations on
@RequestParamwithout testing
Missing parameters behave differently than request bodies.
Common Mistakes to Avoid
❌ Forgetting @Validated
❌ Assuming @Valid works for query parameters
❌ Catching validation exceptions inside controllers
❌ Using vague error messages
❌ Skipping validation for “small” parameters
Conclusion
Validating query parameters is a critical skill for building robust Spring Boot APIs.
With:
@Validated- Bean Validation annotations
- Clean controller design
you can ensure your APIs fail fast, fail safely, and communicate clearly.
Mastering validating query parameters in Spring Boot will strengthen your Java programming foundation and help you learn Java with real-world backend best practices.
Call to Action
💬 Have questions about validation or error handling?
👇 Drop them in the comments below!
🔗 Helpful Resources
Top comments (0)