Spring Boot Optional Path Variables

It is common in Spring Boot to perform more than one task in a single controller method. In this short 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 impossible 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 be 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 almost duplicate code blocks.

Using required Attribute

If you are using Spring Boot 2 and higher, the easiest and most 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 exceptions.

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 me on Twitter and LinkedIn. You can also subscribe to RSS Feed.

You might also like...

Digital Ocean

The simplest cloud platform for developers & teams. Start with a $200 free credit.

Buy me a coffee ☕

If you enjoy reading my articles and want to help me out paying bills, please consider buying me a coffee ($5) or two ($10). I will be highly grateful to you ✌️

Enter the number of coffees below:

✨ Learn to build modern web applications using JavaScript and Spring Boot

I started this blog as a place to share everything I have learned in the last decade. I write about modern JavaScript, Node.js, Spring Boot, core Java, RESTful APIs, and all things web development.

The newsletter is sent every week and includes early access to clear, concise, and easy-to-follow tutorials, and other stuff I think you'd enjoy! No spam ever, unsubscribe at any time.