It is quite common in Spring Boot to perform more than one task in a single controller method. In this quick article, you'll learn how to use optional path variables in Spring Boot to handle more than one type of request.

By design, in Spring Boot, it is not possible to have optional path variables. Let us say you have the following controller method that handles GET requests on the /todos/{id} endpoint:

@GetMapping(value = {"/todos", "/todos/{id}"})
public @ResponseBody Object fetchTodos(@PathVariable Long id) {
    if (id == null) {
        // return single todo
        return todoRespository.findById(id);
    } else {
        // return all todos
        return todoRespository.findAll();
    }
}

We want to make sure that the above controller method works for both /todos and /todos/1 requests. If the id path variable is present, we just fetch a todo by ID and send it back to the client. Otherwise, simply returns all todos.

Right now, if you skip the id path variable and call the /todos endpoint directly, Spring Boot will throw an exception. One way to avoid this exception is to have two methods, one with the path variable and then another without as shown below:

// two methods - not recommended
@GetMapping("/todos/{id}")
public @ResponseBody Todo fetchTodoById(@PathVariable Long id) {
    return todoRespository.findById(id);
}

@GetMapping("/todos")
public @ResponseBody List<Todo> fetchAllTodos() {
    return todoRespository.findAll();
}

Technically, the above approach should work fine but what if you have a bunch of optional path variables? This approach can quickly lead to a large number of (almost) duplicate code-blocks.

Using required Attribute

If you are using Spring Boot 2 and higher, the easiest and efficient way to make any @PathVariable optional is by setting the required attribute to false as shown below:

@GetMapping(value = {"/todos", "/todos/{id}"})
public @ResponseBody Object fetchTodos(@PathVariable(required = false) Long id) {
    if (id == null) {
        return todoRespository.findById(id);
    } else {
        return todoRespository.findAll();
    }
}

That's all you need to do. Now, the above method can handle both types of requests without any exception.

Using Java 8 Optional Class

Another way to make a path variable optional in Spring Boot (with Spring 4+) is by using the Java 8 Optional class:

@GetMapping(value = {"/todos", "/todos/{id}"})
public @ResponseBody Object fetchTodos(@PathVariable Optional<Long> id) {
    if (id.isPresent()) {
        return todoRespository.findById(id.get());
    } else {
        return todoRespository.findAll();
    }
}

The Optional class is a special container object which may or may not contain a non-null value. If a value is present, the isPresent() method will return true and get() will return the value.

✌️ Like this article? Follow @attacomsian on Twitter. You can also follow me on LinkedIn and DEV. Subscribe to RSS Feed.

👋 If you enjoy reading my articles and want to support me to continue creating free tutorials, ☕ Buy me a coffee (cost $5).