Lesson 3
Handling Path Variables and Query Parameters
Introduction

Welcome! In this lesson, we'll dive into Handling Path Variables and Query Parameters in Spring Boot. You've already learned how to create REST endpoints and return JSON responses. Now, we’ll extend that knowledge by exploring how to handle path variables and query parameters in your APIs.

Path variables and query parameters are crucial for passing information and filtering data in RESTful APIs. These tools will make your endpoints more dynamic and powerful. Let’s get started!

Understanding Path Variables

In previous lessons, we used hardcoded endpoints like /recipes/american-sandwich. As your application grows and you have a database with thousands of recipes, defining a separate endpoint for each recipe becomes impractical. Instead, you can use path variables, such as: /recipes/{recipeId}. Here, recipeId is a dynamic part of the URL that gets passed to your methods. For example:

  • When someone requests /recipes/123, the recipeId variable will take the value 123.
  • When someone requests /recipes/pizza-pepperoni, the recipeId variable will take the value pizza-pepperoni.

You can also have paths with multiple path variables. For instance, by introducing recipe categories, you can have the following paths:

  • /categories/{recipeCategory}
  • /categories/{recipeCategory}/recipes/{recipeId}

This hierarchical structure makes your API more organized and reflective of your domain model.

Path Variables Example

Imagine you have a collection of recipes, and you want to retrieve a specific recipe by its unique ID. Here’s how you can implement this in Spring Boot:

Java
1@GetMapping("/recipes/{recipeId}") 2public Recipe getRecipeById(@PathVariable Long recipeId) { 3 return recipeRepository.findById(recipeId) 4 .orElseThrow(() -> new IllegalArgumentException("Recipe not found")); 5}

Here’s what’s happening:

  1. @GetMapping("/recipes/{recipeId}): Maps HTTP GET requests to /recipes/{recipeId}. {recipeId} is a path variable.
  2. @PathVariable Long recipeId: Binds the path variable recipeId from the URL to the method parameter.
  3. The method interacts with recipeRepository to find a recipe by its ID.
  4. If no matching recipe is found, it throws an IllegalArgumentException.

Path variables make your URLs more dynamic and informative.

Named Path Variables

In the previous example, we assumed the template path variable and method parameter names were the same. But if they're different, you can specify the name in the @PathVariable annotation:

Java
1@GetMapping("/recipes/{recipeId}") 2public Recipe getRecipe(@PathVariable("recipeId") Long id) { 3 return recipeRepository.findById(id) 4 .orElseThrow(() -> new IllegalArgumentException("Recipe not found")); 5}

Here’s what’s happening:

  1. @GetMapping("/recipes/{recipeId}): Maps HTTP GET requests to /recipes/{recipeId}.
  2. @PathVariable("recipeId") Long id: Binds the path variable recipeId from the URL to the method parameter id.
  3. The rest works as before.

This approach provides clarity and flexibility when the names differ.

Understanding Query Parameters

Query parameters pass additional information to endpoints, typically for filtering or sorting data. They are added to the URL after a question mark and help narrow down search results. Here are some examples:

  • /recipes?diet=vegan: Filters recipes by diet type, showing only vegan recipes.
  • /recipes?diet=vegan&complexity=easy: Filters recipes by diet type and complexity, showing only easy vegan recipes.
  • /recipes?diet=vegan&complexity=easy&prepTime=30: Filters recipes by diet type, complexity, and prep time, showing only easy vegan recipes that take 30 minutes or less to prepare.
Query Parameters Example

Suppose you want to filter recipes by type (e.g., vegan, vegetarian). Here’s how to implement this:

Java
1@GetMapping("/recipes") 2public List<Recipe> getRecipesByType(@RequestParam String type) { 3 return recipeService.getRecipesByType(type); 4}

Here's what’s happening:

  1. @GetMapping("/recipes"): Maps HTTP GET requests to /recipes.
  2. @RequestParam String type: Binds the query parameter type to the method parameter.
  3. The method calls getRecipesByType in recipeService to get recipes filtered by type.

Query parameters enable more detailed and specific requests to your API.

Making Query Parameter Optional

Sometimes, you may want to allow a query parameter to be optional. This means that the request can still be processed even if the query parameter is not provided. To make a query parameter optional, you can provide a default value or make it nullable:

Java
1@GetMapping("/recipes") 2public List<Recipe> getRecipesByType(@RequestParam(required = false) String type) { 3 return recipeService.getRecipesByType(type); 4}

Here’s what’s happening:

  1. @RequestParam(required = false) String type: Binds the query parameter type to the method parameter and allows it to be null if not provided.
  2. The method getRecipesByType will handle the case where type is null appropriately.

This makes the query parameter optional and your API more flexible.

Making Query Parameter Optional with java.util.Optional

Another way to make a query parameter optional is by using java.util.Optional. This approach provides a more explicit way of dealing with the presence or absence of a query parameter:

Java
1@GetMapping("/recipes") 2public List<Recipe> getRecipesByType(@RequestParam Optional<String> type) { 3 return recipeService.getRecipesByType(type.orElse(null)); 4}

Here’s what’s happening:

  1. @RequestParam Optional<String> type: Binds the query parameter type to an Optional type, making it clear that the parameter may or may not be present.
  2. The method getRecipesByType gets the value from the Optional or null if the parameter is not provided.

Using Optional provides a clean and explicit way to handle optional query parameters, enhancing code readability and maintainability.

Named Query Parameters

When the query parameter name differs from the method parameter name, you can explicitly name the query parameter. Here’s how:

Java
1@GetMapping("/recipes") 2public List<Recipe> getRecipes(@RequestParam("type") Optional<String> recipeType) { 3 return recipeService.getRecipesByType(recipeType); 4}

Here's what’s happening:

  1. @GetMapping("/recipes"): Maps HTTP GET requests to /recipes.
  2. @RequestParam("type") Optional<String> recipeType: Binds the query parameter type from the URL to the method parameter recipeType.

This approach provides flexibility when names differ.

Combining Path Variables and Query Parameters

You can combine path variables and query parameters in a single endpoint. This is useful when you need to identify a specific resource category and apply some filters or additional criteria.

Here’s an endpoint that retrieves recipes by category and allows filtering for a specific dietary preference:

Java
1@GetMapping("/category/{recipeCategory}") 2public List<Recipe> getRecipesByCategoryAndDietaryPreference(@PathVariable String recipeCategory, @RequestParam Optional<String> dietaryPreference) { 3 List<Recipe> recipes = recipeRepository.findByCategory(recipeCategory); 4 5 if (dietaryPreference.isPresent()) { 6 return recipes.stream() 7 .filter(recipe -> recipe.getDietaryPreference().equalsIgnoreCase(dietaryPreference.get())) 8 .collect(Collectors.toList()); 9 } 10 11 return recipes; 12}

This example shows how combining path variables and query parameters can create powerful and flexible endpoints. You can now retrieve all recipes within a specific category and further filter them based on dietary preference.

Summary

In this lesson, we've explored how to handle path variables and query parameters in Spring Boot. You’ve learned:

  • The importance of path variables and how to use them
  • How query parameters can make your endpoints more versatile
  • Using named path variables and query parameters for clarity and flexibility
  • Combining path variables and query parameters for dynamic requests

In the upcoming exercises, you’ll practice these concepts hands-on. Understanding how to use path variables and query parameters effectively will make your APIs more dynamic and efficient. Good luck!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.