# Unit 10: Path Parameters and Validations

## Introduction

The `Annotated` type, introduced in Python 3.9 from the `typing` module, allows for extending type hints with Python objects, which can be particularly useful for providing extra metadata or validation in FastAPI.

With the `Annotated` type, you can add metadata to your path parameters, making your API self-documenting and enabling automatic request validation. This is particularly useful for enhancing the functionality of path parameters with validations or constraints directly in your FastAPI endpoints.

***

## Path Parameters

Let's take a quick look of using query parameter in FastAPI:

{% code lineNumbers="true" %}

```python
from fastapi import FastAPI, Query, Path
from typing import Annotated

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
    q: Annotated[str | None, Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
```

{% endcode %}

In the above program, the `title` is a metadata of the `item_id` path parameter. And you should note that the query parameter `q` is defined by an *alias* `item-query`, it thus makes the URL to this API endpoint is as follows:

```
http://127.0.0.1:8000/items/111?item-query=Sample
```

## Order the parameters

Considering the following error program that produces an error message `Non-default argument follows default argument` since Python does not allow you to put a value with a `default` value before a value that doesn't have one.

<pre class="language-python" data-line-numbers><code class="lang-python"><strong>// NOTE: THIS ERROR PROGRAM IS USED FOR DEMONSTRATION
</strong>
from fastapi import FastAPI, Query, Path
from typing import Annotated

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(
    item_id: int = None,
    <a data-footnote-ref href="#user-content-fn-1">q: Annotated[str, Query(alias="item-query")]</a>,
):
    results = {"item_id": item_id}
    print(q)
    if q:
        results.update({"q": q})
    return results
</code></pre>

To overcome this issue, you can re-order them, and have the value without a default value (the query parameter `q`) first. But it doesn't matter for FastAPI. It will detect the parameters by their names, types and default declarations (`Query`, `Path`, etc), since it doesn't care about the order.

## Number validations with `Annotated`

Let's take a look in the below program:

<pre class="language-python" data-line-numbers><code class="lang-python">from fastapi import FastAPI, Query, Path
from typing import Annotated

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(
    item_id: Annotated[int, Path(title="The ID of the item to get", <a data-footnote-ref href="#user-content-fn-2">ge=1, le=1000</a>)],
    q: Annotated[str | None, Query(alias="item-query")],
):
    results = {"item_id": item_id}
    print(q)
    if q:
        results.update({"q": q})
    return results
</code></pre>

{% hint style="info" %}
Program explanation:

* `item_id`: This is a path parameter. It's expected to be an integer (`int`) between 1 and 1000 (inclusive). The `Path` function provides extra information and validation for this parameter.
* `q`: This is a query parameter. It's expected to be a string (`str`) or `None`. The `Query` function provides an alias for this parameter, so in the URL it will be `item-query` instead of `q`.
  {% endhint %}

You can declare numeric validations by using following syntaxes:

* `gt`: `g`reater `t`han
* `ge`: `g`reater than or `e`qual
* `lt`: `l`ess `t`han
* `le`: `l`ess than or `e`qual

Let's take a look at another example:

{% code lineNumbers="true" %}

```python
from fastapi import FastAPI, Path, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
    *,
    item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
    q: str,
    size: Annotated[float, Query(gt=0, lt=10.5)],
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
```

{% endcode %}

You should note that:

* The asterisk (`*`) marks the end of positional arguments; all following `args` must be keyword-only. Without the `*`, you could accidentally call the function like `read_items(5, "search", 3.5)`. With the `*`, you must use keyword names `read_items(item_id=5, q="search", size=3.5)`.
* Number validations also work for `float` values.
* `gt` is more strictly than `ge`. So, `0.5` would be a valid value, but `0.0` or `0` would not.

***

## Summary

Using `Annotated` with FastAPI allows for sophisticated validation and metadata enhancements directly in the function signatures of your API endpoints. This can significantly improve the robustness, readability, and documentation of your API by leveraging Python's type hints and FastAPI's powerful validation capabilities.

[^1]: Non-default argument follows default argument

[^2]: Number validations


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://fastapi-docs.duonghuuphuc.com/unit-10-path-parameters-and-validations.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
