What is the difference between @RequestParam and @PathVariable, and when do you use each?
answer
- path template {id} vs query ?id=
- PathVariable = resource identity; RequestParam = filter/sort/page
- PathVariable required (else 404); RequestParam required-by-default (else 400)
- both auto-convert String -> target type
- explicit name needed without -parameters
basics
~20 s@PathVariable pulls a value out of the URL path itself (e.g. /users/42 -> id=42). @RequestParam reads a query-string parameter after the ? (e.g. /users?id=42) or a form field. You use @PathVariable to identify a resource, @RequestParam to filter/paginate/search.
solid answer
~30 s@PathVariable binds a segment of the URI path declared in the mapping template, like @GetMapping("/users/{id}") -> @PathVariable Long id — it identifies a specific resource. @RequestParam binds a query-string parameter (?page=2) or, for form POSTs, an application/x-www-form-urlencoded body field. Both do automatic type conversion (String -> Long, enum, etc.). @RequestParam supports required, defaultValue, and Optional; @PathVariable is required by default because a missing segment means the URL simply doesn't match the mapping. Rule of thumb: path variables for hierarchical resource identity (REST style), request params for optional modifiers like filtering, sorting, and pagination.
code
java · 17 lines@RestController
@RequestMapping("/users")
class UserController {
// GET /users/42 -> id = 42 (path identifies the resource)
@GetMapping("/{id}")
public User byId(@PathVariable Long id) {
return service.find(id);
}
// GET /users?status=ACTIVE&page=2 -> query params filter/paginate
@GetMapping
public List<User> list(@RequestParam(required = false) Status status,
@RequestParam(defaultValue = "0") int page) {
return service.search(status, page);
}
}go deeper
Must nail the path-vs-query distinction and give a correct URL example for each.
Should mention required-by-default behavior and type conversion for both.
Should articulate the REST convention (identity vs modifier) and the 404-vs-400 distinction.
Frames it as URI design / API contract and knows the argument-resolver machinery underneath.
Spring MVC controllers are classes annotated with @Controller or @RestController whose handler methods are mapped to HTTP requests via @RequestMapping (or shortcuts @GetMapping, @PostMapping, etc.). Method parameters are populated by *argument resolvers*; @RequestParam, @PathVariable, @RequestHeader, and @CookieValue are the annotations that tell Spring where to pull a simple (scalar) value from. **@PathVariable** extracts a variable embedded in the URI *path template*. You declare a placeholder in the mapping with curly braces and bind it: ```java @GetMapping("/users/{id}") public User get(@PathVariable Long id) { ... } ``` For GET /users/42, `id` becomes the Long 42. The name is matched by parameter name; if the code isn't compiled with parameter names you must be explicit: @PathVariable("id"). Path variables are *required by definition* — if the segment is absent the URL doesn't match the template at all, so you'd get a 404, not a missing-value error. You can capture multiple variables (/orders/{orderId}/items/{itemId}) and even bind them all at once into a Map<String,String> with @PathVariable Map<String,String> vars. **@RequestParam** binds a *query-string* parameter (the part after ?, e.g. /search?q=spring&page=2) or a field from an application/x-www-form-urlencoded request body (a classic HTML form POST). It also binds multipart file parts (MultipartFile). Example: ```java @GetMapping("/search") public Page<Item> search(@RequestParam String q, @RequestParam(defaultValue = "0") int page) { ... } ``` @RequestParam is *required by default*; a missing param throws MissingServletRequestParameterException -> HTTP 400. You relax that with required = false, defaultValue = "..." (which also implies not-required), or by declaring the type as Optional<T> or a nullable type. **Type conversion**: both annotations run the incoming String through Spring's conversion machinery (WebConversionService / ConversionService), so String, primitives and wrappers, enums, and types with a registered Converter/Formatter (e.g. LocalDate via the DateTimeFormat support) all bind automatically. A value that can't be converted (e.g. ?page=abc for an int) yields a 400 (MethodArgumentTypeMismatchException). **When to use which**: follow REST conventions. Use @PathVariable for the *identity/hierarchy* of the resource you're acting on — /users/{id}, /users/{id}/orders/{orderId}. Use @RequestParam for *optional modifiers* that don't identify the resource — filtering (?status=ACTIVE), searching (?q=...), sorting (?sort=name), and pagination (?page=&size=). A useful test: if the value names *which thing*, it's a path variable; if it *narrows or shapes the response*, it's a request param. **Common gotchas**: (1) Forgetting to compile with -parameters (Kotlin/Java) so implicit name binding fails — Spring Boot enables this by default, but plain setups may need it. (2) Using @RequestParam to read a JSON body — it won't; JSON goes to @RequestBody. (3) Trailing/encoding surprises: a path variable stops at the next / by default, so an id containing a slash needs a regex in the template like {path:.+}.
- If GET /users/42 returns 404, name two likely causes related to @PathVariable.Either the mapping template doesn't declare {id} (or the name doesn't match the parameter and no explicit @PathVariable("id") is given), or the id contains a '/' that the default single-segment matcher won't capture without a regex like {id:.+}. A conversion failure would be 400, not 404.
- Can @RequestParam read a value from a POST body?Yes, if the body is application/x-www-form-urlencoded (a classic HTML form) or multipart/form-data — @RequestParam binds those fields, and MultipartFile for file parts. It does NOT read JSON bodies; those require @RequestBody.
saying these in an interview costs you the question
- Claiming @PathVariable reads the query string (it reads the path template).
- Saying @RequestParam is not required by default (it IS required unless you set required=false, defaultValue, or Optional).
- Thinking @RequestParam can bind a JSON request body.