Path Parameters
In an HTTP request, the URL path plays a central role in routing. It's how the server knows what resource the client is trying to access. But paths aren't just static—they often carry dynamic values known as path parameters.
What Do Path Parameters Look Like?
A path parameter is a variable part of the URL path. It’s typically used to identify a specific resource. For example:
GET /users/42 HTTP/1.1
Host: example.com
In this example:
- /users/42 is requesting the user whose ID is 42. The 42 is a path parameter—data embedded directly in the URL structure.
Although we can't be entirely sure about what are the path variables in an url, a restful api usually follows the pattern of
/entity_name_in_plural/{entity_id}/sub_entity_name_in_plurals/{sub_entity_id}/...
Here, users
likely refers to the User
entity, and 42
is the value of its ID.
Path Parameters vs Other Request Parameters
Path parameters carry one-dimensional, fixed values like IDs, slugs, or names—ideal for identifying a single resource.
-
Location in Request Path parameters are part of the URL path itself, embedded directly within the route structure:
GET /users/42 HTTP/1.1
-
Encoding Format Path parameters are URL-encoded as part of the path segment. Special characters are percent-encoded, but they do not appear after a ?—they are part of the URL hierarchy.
-
Data Types Path parameters represent single, fixed values and support primitive types such as:
str, int, float, bool
Unlike query parameters, path parameters cannot represent arrays or repeated keys. They always carry exactly one value per parameter.
-
Purpose Path parameters typically identify a specific resource or entity by embedding identifiers in the URL path. In contrast, query parameters are used for filtering, pagination, or optional data.
Declare Path Parameters in lihil
Implicitly Declare a Path Param
In lihil
, path parameters are first-class citizens.
you can declare path parameters implicitly by including them in the route path and matching function argument names:
For example:
from lihil import Route
user = Route("/users/{user_id}")
@user.get
async def get_user(user_id: int) -> User:
...
Here, user_id
is implicitly declared because it matches the {user_id}
placeholder in the route path.
lihil automatically extracts and converts it from the URL.
For example, If a request like GET /users/42 comes in, lihil:
- extracts 42 from the URL,
- converts it to int (based on the type hint),
- and passes it to the get_user function.
You don’t need to parse strings or access the raw request manually—lihil handles it.
Explictly Declare a Path Param with lihil.Param
from lihil import Route, Param
from typing import Annotated
PositiveInt = Annotated[int, Param(gt=0)]
user = Route("/users/{user_id}")
@user.get
async def get_user(user_id: PositiveInt) -> User:
...
Type validation
Path parameters are validated based on their type hints. If you declare a path parameter as an int
, lihil will automatically convert it to an integer. If the conversion fails, lihil will return a 422 Invalid Request error.
If a union of types is provided, lihil will try to convert the value to each type in the union until one succeeds. If none succeed, a 422 error is returned.
If a union contains str or bytes, the conversion will always succeed, as these types can represent any value. This means that if you have a union like Union[int, str]
, the path parameter will always be treated as a string.
Data Validation
You might also want to validate the value of the path parameter. For example, if you want to ensure that the user ID is positive, you can set such constraints using lihil.Param
.
from lihil import Route, Param
PositiveInt = Param(gt=0)
user = Route("/users/{user_id}")
@user.get
async def get_user(user_id: PositiveInt) -> User: ...
lihil.Param
allows you to set various constraints on the path parameter, such as minimum and maximum values, regex patterns, and more. This is particularly useful for ensuring that the data you receive is valid before processing it.
lihil.Param
uses msgspec.Meta
under the hood, so you can use all the features of msgspec.Meta
to validate your path parameters.
Custom validation
You can also create custom validators for path parameters. This is useful when you have complex validation logic that can't be expressed with simple constraints.
from lihil import Route, Param, HTTPException
class MyError(HTTPException[T]):
"I don't like this value"
def decoder(value: str) -> int:
# Custom decoding logic
if value == "42":
raise MyError("I don't like this value")
return int(value)
async def create_user(user_id: int, user_age: Annotated[int, Param(decoder=decoder)]) -> User:
# Your logic here
pass
Recap
- Path parameters are embedded directly in the URL path and identify specific resources.
- They carry exactly one value per parameter and support primitive types like int, str, and bool.
- In lihil, you can declare path parameters implicitly in the route path or explicitly with Param for validation and constraints.
- Custom decoders allow you to implement advanced validation and error handling for path parameters.