java
27 lines · 6 steps
Binding collection query params in Spring
A Spring REST endpoint maps repeated and comma-separated query parameters into typed collections and folds them into a filter.
Explained by
highlit
1@RestController
2@RequestMapping("/api/products")
3public class ProductSearchController {
4
5 private final ProductQueryService productQueryService;
6
7 public ProductSearchController(ProductQueryService productQueryService) {
8 this.productQueryService = productQueryService;
9 }
10
11 @GetMapping
12 public List<ProductView> search(
13 @RequestParam(name = "tags", required = false) List<String> tags,
14 @RequestParam(name = "ids", defaultValue = "") Set<Long> ids,
15 @RequestParam(name = "status", required = false) EnumSet<ProductStatus> statuses) {
16
17 ProductFilter filter = ProductFilter.builder()
18 .tags(tags == null ? List.of() : tags)
19 .ids(ids)
20 .statuses(statuses == null ? EnumSet.allOf(ProductStatus.class) : statuses)
21 .build();
22
23 return productQueryService.findMatching(filter).stream()
24 .map(ProductView::from)
25 .toList();
26 }
27}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Spring can bind a query parameter straight into a List, Set, or EnumSet, converting each element to the target type automatically.
- 2Defaulting absent optional parameters at the controller boundary keeps downstream services free of null checks.
- 3Mapping entities to view objects before returning keeps the API contract decoupled from the persistence model.
Related explainers
python
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from pydantic import BaseModel, EmailStr from sqlalchemy.orm import Session
Building a signup endpoint in FastAPI
dependency-injection
request-validation
background-tasks
Intermediate
8 steps
java
public interface GitHubClient { @GetExchange("/users/{username}") GitHubUser getUser(@PathVariable String username);
Declarative HTTP clients in Spring
http-client
declarative-api
proxy
Intermediate
8 steps
java
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
Building a trie for autocomplete in Java
trie
prefix-tree
recursion
Intermediate
8 steps
php
<?php namespace App\Providers;
Subdomain multi-tenancy routing in Laravel
multi-tenancy
service-container
route-binding
Advanced
7 steps
java
import java.util.ArrayDeque; import java.util.Deque; import java.util.Map;
Evaluating math expressions with two stacks
stacks
parsing
operator-precedence
Intermediate
9 steps
java
@Entity @Table(name = "orders") @SQLDelete(sql = "UPDATE orders SET deleted = true, deleted_at = now() WHERE id = ?") @Where(clause = "deleted = false")
Soft deletes with Hibernate in Spring
soft-delete
jpa
hibernate
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/binding-collection-query-params-in-spring-explained-java-a6e3/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.