Addresses, path variables and query parameters
One list address is not enough: you need to open a particular post and filter the list. A post id is usually passed as part of the path — /api/posts/2 — and optional conditions in the query string after ?: /api/posts?author=anna. Spring can put both kinds of parameters straight into method arguments.
A common prefix
To avoid repeating /api/posts in every method, the address goes above the class:
// src/main/java/com/example/blog/PostController.java
@RestController
@RequestMapping("/api/posts")
public class PostController {
private final PostService postService;
public PostController(PostService postService) {
this.postService = postService;
}
@GetMapping
public List<Post> list() {
return postService.findAll();
}
}@RequestMapping("/api/posts") sets the prefix for all methods of the class, and an empty @GetMapping means the prefix itself, that is GET /api/posts.
A path variable
// src/main/java/com/example/blog/PostController.java
@GetMapping("/{id}")
public Post show(@PathVariable Long id) {
return postService.findById(id);
}Curly braces in the address declare a variable part. @PathVariable puts its value into the argument with the same name and converts the text "2" into a Long itself. In the service we add a lookup by id:
// src/main/java/com/example/blog/PostService.java
public Post findById(Long id) {
return repository.findById(id)
.orElseThrow(() -> new PostNotFoundException(id));
}// src/main/java/com/example/blog/PostNotFoundException.java
package com.example.blog;
public class PostNotFoundException extends RuntimeException {
public PostNotFoundException(Long id) {
super("Post " + id + " not found");
}
}The request curl http://localhost:8080/api/posts/2 returns post number 2. A missing id gives a 500 error for now — in the error handling lesson we will turn it into a clear 404 response.
A query parameter
// src/main/java/com/example/blog/PostController.java
@GetMapping
public List<Post> list(@RequestParam(required = false) String author) {
return postService.findAll(author);
}// src/main/java/com/example/blog/PostService.java
public List<Post> findAll(String author) {
return repository.findAll().stream()
.filter(post -> author == null || post.author().equals(author))
.sorted(Comparator.comparing(Post::id))
.limit(properties.pageSize())
.toList();
}@RequestParam takes the author value from the query string. Without required = false the parameter would be mandatory. Once posts exist, curl "http://localhost:8080/api/posts?author=anna" returns only Anna's posts:
[{"id":1,"title":"Hello, Spring","body":"First post","author":"anna","createdAt":"2026-10-04T08:50:07.494425300Z"}]The address is in quotes because the shell may interpret ? and & its own way.
A default value
@RequestParam(defaultValue = "0") int pageIf the parameter is missing, Spring uses 0. Such a parameter is optional already.
Annotations for HTTP methods
| Annotation | Request | Purpose |
|---|---|---|
@GetMapping | GET | Read data |
@PostMapping | POST | Create |
@PutMapping | PUT | Replace as a whole |
@PatchMapping | PATCH | Change a part |
@DeleteMapping | DELETE | Delete |
Common mistake
If the path contains text instead of a number, for example /api/posts/abc, Spring cannot convert it to Long and answers 400:
{"timestamp":"2026-10-04T08:50:07.919Z","status":400,"error":"Bad Request","path":"/api/posts/abc"}The log shows Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'. This is not a bug in your code — the client sent a wrong address.