Java Spring
REST API
Optional `Parameters`
Backend Development
Java Programming

Java Spring REST API Handling Many Optional `Parameters`

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Spring makes it easy to accept optional query parameters, but controller methods become hard to maintain when every filter is declared as a separate argument. Once an endpoint grows beyond a few optional fields, the better pattern is to group those filters into a request object and keep parsing, validation, and query construction separate.

Start Simple With @RequestParam

For a small endpoint, ordinary optional request parameters are fine. Mark them as not required and keep the method readable.

java
1@RestController
2@RequestMapping("/products")
3public class ProductController {
4
5    @GetMapping
6    public List<ProductDto> listProducts(
7            @RequestParam(required = false) String category,
8            @RequestParam(required = false) Integer minPrice,
9            @RequestParam(required = false) Integer maxPrice) {
10
11        return List.of();
12    }
13}

This works well for a handful of filters. It breaks down when the method also needs paging, sorting, validation, and feature-specific options.

Use a Parameter Object for Real APIs

Once the endpoint has many optional parameters, bind them into one object. That keeps the controller signature stable and makes validation easier.

java
1public class ProductSearchParams {
2    private String category;
3    private Integer minPrice;
4    private Integer maxPrice;
5    private String sortBy;
6    private Integer page = 0;
7    private Integer size = 20;
8
9    public String getCategory() { return category; }
10    public void setCategory(String category) { this.category = category; }
11    public Integer getMinPrice() { return minPrice; }
12    public void setMinPrice(Integer minPrice) { this.minPrice = minPrice; }
13    public Integer getMaxPrice() { return maxPrice; }
14    public void setMaxPrice(Integer maxPrice) { this.maxPrice = maxPrice; }
15    public String getSortBy() { return sortBy; }
16    public void setSortBy(String sortBy) { this.sortBy = sortBy; }
17    public Integer getPage() { return page; }
18    public void setPage(Integer page) { this.page = page; }
19    public Integer getSize() { return size; }
20    public void setSize(Integer size) { this.size = size; }
21}
java
1@GetMapping
2public List<ProductDto> listProducts(@ModelAttribute ProductSearchParams params) {
3    return productService.search(params);
4}

Now the controller just receives input. The service can decide how each optional field affects the query.

Build Queries From Present Values Only

The service layer should treat optional parameters as filters that may or may not exist. Here is a simple example using conditional logic:

java
1@Service
2public class ProductService {
3
4    public List<ProductDto> search(ProductSearchParams params) {
5        if (params.getMinPrice() != null && params.getMaxPrice() != null
6                && params.getMinPrice() > params.getMaxPrice()) {
7            throw new IllegalArgumentException("minPrice cannot exceed maxPrice");
8        }
9
10        // In a real application, translate present fields into a repository query.
11        return List.of();
12    }
13}

This keeps parsing concerns in the controller and business rules in the service. If you later move to JPA Specifications, Querydsl, or a search backend, the controller does not have to change.

Default Values and Validation

Optional does not mean unvalidated. You can still enforce ranges and sensible defaults.

java
1public class ProductSearchParams {
2    @Min(0)
3    private Integer page = 0;
4
5    @Min(1)
6    @Max(100)
7    private Integer size = 20;
8
9    private String category;
10}

Use defaults only where they match business meaning. A default page size is reasonable. A default category or default date range can silently surprise API consumers if it changes what "no filter" should mean.

Common Pitfalls

  • Putting ten or fifteen @RequestParam values directly in one controller method and creating an unreadable signature.
  • Using Optional fields in request DTOs. Spring can bind them, but plain nullable fields are often simpler.
  • Applying defaults that change business meaning instead of only filling in transport-level conveniences such as page size.
  • Mixing validation, parsing, and query logic in the controller.
  • Letting contradictory filters through, such as a minimum price greater than a maximum price.

Summary

  • Use plain optional @RequestParam values only for small endpoints.
  • Move larger filter sets into a parameter object bound with @ModelAttribute.
  • Keep controller code thin and build query logic in the service layer.
  • Validate optional values just as carefully as required ones.
  • Choose defaults only when they represent stable, predictable API behavior.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.