Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 page

If the parameter is missing, Spring uses 0. Such a parameter is optional already.

Annotations for HTTP methods

AnnotationRequestPurpose
@GetMappingGETRead data
@PostMappingPOSTCreate
@PutMappingPUTReplace as a whole
@PatchMappingPATCHChange a part
@DeleteMappingDELETEDelete

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.

Thanks for reading!

Donate to the site — we work hard for you.