Java
9/6/2026
8 min read

@PathVariable in Spring Boot: How to Use It, With Examples

@PathVariable in Spring Boot: How to Use It, With Examples

@PathVariable is how a Spring Boot controller reads a value out of the URL path. When a request comes in for /users/101, @PathVariable is what turns 101 into a Java variable your method can use.

It is a small annotation with a few sharp edges: optional values, name mismatches, type conversion failures, and the question of when a value belongs in the path at all rather than the query string. This guide covers the basic use, then each of those.

What @PathVariable Does

@PathVariable binds a segment of the request URL to a method parameter in a controller. The path template declares a placeholder in braces, and the annotation pulls the matching segment out.

It is used for identifying a specific resource. In /users/101, the 101 says which user. That is different from a query parameter such as /users?active=true, which filters or modifies a request rather than identifying a resource.

Add this to a controller class, in a file such as UserController.java:

@GetMapping("/users/{userId}")
public String getUser(@PathVariable Long userId) {
    return "User ID: " + userId;
}

When a request arrives for /users/101, Spring matches the {userId} placeholder, converts the string 101 to a Long, and passes it in. The parameter name and the placeholder name match, so no extra configuration is needed.

Using Multiple Path Variables

Nested resources need more than one. An ecommerce API fetching a specific product for a specific user reads both segments:

@GetMapping("/users/{userId}/products/{productId}")
public String getUserProduct(@PathVariable Long userId,
                             @PathVariable Long productId) {
    return "User: " + userId + ", Product: " + productId;
}

The URL now describes the relationship as well as the identifiers, which is most of what makes a REST API readable.

Keep this under control. Three path variables in one endpoint is usually a sign the resource hierarchy is wrong, not that you need a fourth.

When the Parameter Name Does Not Match

If the method parameter is named differently from the placeholder, name the placeholder explicitly:

@GetMapping("/users/{userId}/orders/{orderId}")
public String getOrder(@PathVariable(name = "userId") Long id,
                       @PathVariable Long orderId) {
    return "User: " + id + ", Order: " + orderId;
}

This matters more than it looks. Spring resolves parameter names by reflection, and that only works when the class is compiled with the -parameters flag. Without it, names are erased to arg0, arg1, and binding fails at runtime with a message about a missing URI template variable. Spring Boot's Maven and Gradle plugins set the flag by default, but hand-rolled builds often do not.

The safe habit: name the variable explicitly whenever it differs from the parameter, and consider naming it always.

Capturing All Path Variables in a Map

When the set of variables is not fixed, bind them all at once:

@GetMapping("/users/{userId}/address/{addressId}")
public String getAddress(@PathVariable Map<String, String> pathVars) {
    return "User: " + pathVars.get("userId") +
           ", Address: " + pathVars.get("addressId");
}

Every value arrives as a String, so you convert by hand and you lose the automatic type checking. Use this only for genuinely dynamic routing. For anything with a known shape, strongly typed parameters are better.

Optional Path Variables

A path variable is required by default. If the segment is missing, the route does not match and Spring returns 404 rather than calling your method with a null.

If you want one endpoint to serve both /users and /users/101, declare both templates and mark the variable as not required:

@GetMapping({"/users", "/users/{userId}"})
public String getUsers(@PathVariable(required = false) Long userId) {
    if (userId == null) {
        return "All users";
    }
    return "User ID: " + userId;
}

Optional<Long> works too and reads better in modern code:

@GetMapping({"/users", "/users/{userId}"})
public String getUsers(@PathVariable Optional<Long> userId) {
    return userId.map(id -> "User ID: " + id).orElse("All users");
}

Two endpoints are usually clearer than one endpoint with a branch. Reach for this only when the two responses are genuinely the same shape.

Matching Patterns in the Path

Spring's path matching accepts a regular expression inside the placeholder, which is useful when a segment has a fixed format:

@GetMapping("/invoices/{invoiceId:INV-\\d{6}}")
public String getInvoice(@PathVariable String invoiceId) {
    return "Invoice: " + invoiceId;
}

A request for /invoices/INV-000123 matches. A request for /invoices/hello does not, and never reaches your method. Rejecting malformed input at the routing layer is cheaper than validating it in the handler.

Handling Type Conversion Failures

This is the failure most people hit in production. Given a Long parameter, a request for /users/abc cannot convert, and Spring throws MethodArgumentTypeMismatchException before your method runs. Unhandled, that surfaces as a 500, which is wrong: the client sent a bad request, not the server.

Handle it and return 400, in a file such as GlobalExceptionHandler.java:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<Map<String, String>> handleTypeMismatch(
            MethodArgumentTypeMismatchException ex) {
        Map<String, String> body = Map.of(
            "error", "INVALID_PATH_VARIABLE",
            "message", "&#x27;" + ex.getValue() + "&#x27; is not a valid " + ex.getName()
        );
        return ResponseEntity.badRequest().body(body);
    }
}

Now /users/abc returns a 400 with a message the caller can act on, and your error rate stops lying to you.

Validating Path Variables

Type conversion is not validation. A Long of -5 converts fine and is still not a valid user identifier. Add constraints with Bean Validation:

@RestController
@Validated
public class UserController {

    @GetMapping("/users/{userId}")
    public String getUser(@PathVariable @Min(1) Long userId) {
        return "User ID: " + userId;
    }
}

The @Validated annotation on the class is what activates constraint checking on method parameters. Without it the @Min is ignored silently, which is a common and quiet bug.

Routing is the easy part of a backend.

Stop Being A Junior Developer takes you through one real build, from an empty repo to surviving 50,000 requests a second, in a weekend. Real code, real diagrams, and a circle of engineers doing it with you.

Get the book →

@PathVariable vs @RequestParam

The two are not interchangeable, and choosing wrongly makes an API harder to read for its whole life.

@PathVariable@RequestParam
Position in URLPart of the path: /users/101After the question mark: /users?id=101
PurposeIdentifies a specific resourceFilters, sorts, or paginates
Required by defaultYesYes, but required = false is common
Good forResource identifiersOptional modifiers

The rule of thumb: if removing the value would leave you asking "which one?", it belongs in the path. If removing it would still give you a sensible response covering more results, it belongs in the query string. Pagination, sorting, and filters are query parameters. Identifiers are path variables.

For the query-parameter side in detail, see our guide to @RequestParam in Spring Boot.

Common Mistakes

Too many path variables in one route. More than 2 or 3 usually means the resource hierarchy needs rethinking.

Using a path variable for an optional filter. /users/{status} breaks the moment you want to combine filters. Use a query parameter.

Trusting the value. A path variable is user input. Validate it, and never build a query by string concatenation from it.

Forgetting the conversion error. An unhandled MethodArgumentTypeMismatchException turns a client mistake into a server error and pollutes your monitoring.

Ambiguous routes. /users/{id} and /users/me both match /users/me. Spring prefers the more specific literal path, but relying on that is fragile. Order and name routes so the intent is obvious.

Frequently Asked Questions

What Is @PathVariable in Spring Boot?

An annotation that binds a value from the request URL path to a method parameter in a controller. In /users/101 with a template of /users/{userId}, it passes 101 into the method as userId.

What Is the Difference Between @PathVariable and @RequestParam?

@PathVariable reads a segment of the URL path and identifies a specific resource. @RequestParam reads a query string parameter after the question mark and usually filters or modifies the request. Identifiers go in the path, modifiers go in the query string.

Can a Path Variable Be Optional?

Yes, but it takes work. Declare both URL templates on the mapping and use required = false or Optional<T>. Without both templates, a missing segment simply fails to match the route and returns 404.

Why Is My @PathVariable Null or Failing to Bind?

Almost always a name mismatch. Either the placeholder name differs from the parameter name, or the class was compiled without the -parameters flag so parameter names were erased. Name the variable explicitly: @PathVariable(name = "userId").

How Do I Validate a @PathVariable?

Put @Validated on the controller class and Bean Validation constraints such as @Min on the parameter. Without @Validated on the class, constraints on method parameters are ignored.

Can I Use a Regular Expression in a Path Variable?

Yes. Write it after a colon inside the braces, as in {invoiceId:INV-\\d{6}}. Requests that do not match the pattern never reach your handler.

Summary

@PathVariable pulls resource identifiers out of the URL path, and used well it is what makes a REST API readable. The basic case is one line. The parts that cost people time are the rest: naming the variable explicitly so binding does not depend on compiler flags, returning 400 rather than 500 when conversion fails, adding @Validated so your constraints actually run, and keeping optional filters out of the path where they do not belong.

Get those four right and the annotation stops being something you debug. If you want to practise on a full API rather than a snippet, our backend projects include Spring Boot builds with the frontend already done, and the Java and Spring path covers the framework end to end.

Tags

Enjoyed this article?

Subscribe to our newsletter for more backend engineering insights and tutorials.