There are severl forms of parameter in FastAPI such as Path, Query and so on, we are going to introduce some basic parameter we may frequently use.

Path

To use a path parameter, add a {[PARAM_NAME]} in the path in router decorator like below:

@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
):
    pass

In example above, item_id will become a path parameter.

Notice: Path parameter could NEVER be None, that means you always need to pass a path parameter.

Specified Path

Keep using the example above, imagine we not only have items/id form, but we also want to add a items/stat endpoint which provide another different info such as items current statistics, how to do it?

Here we should know, the priority of the endpoint is determined by their appearance order in the code. So what should we do is to add a items/stat before we declare the previous items/{item_id} function. Below is code example:

@app.get("/items/stat")
async def read_items():
    # do sth here
    pass

@app.get("/items/{item_id}")
async def read_items(
    item_id: Annotated[int, Path(title="The ID of the item to get")],
):
    pass

With code above, when we access item/stat, the request will be handled by the first function.

Query

Based on official docs of FastAPI, if the function parameter is not a path parameter, then it would be considered as a query parameter, like code below:

@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]

In example above, skip and limit are both be considered a query parameter.

Add metadata

To set more limit and add more detail to a query parameter, you can use Python type anno system with Annotated[].

(Notice: Annotated seems to be a new function which just get supported by FastAPI recently, be sure your FastAPI is the newer version if you want to use this feature)

For example, if you want to limit the maximum length of a query parameter, you can write some code like below:

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    # do sth

In example above, we use Annotated and Query to set the max_length limit of param q.

By doing this, FastAPI will:

  • Validate the param
  • Provide full auto-documentation support about those limit

Make Query Required

Basically there are 2 method to make a query param to be required.

First (and maybe the most used way) is quite simple -- just do not set a default value. A query param with no default value will be automatically considered as required.

Alternatively, you can use ... (Ellipsis) grammer to explicitly mark a query param as required like below:

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    # do sth

Required but allow None

This may have a extremely rare situation, that you need to set a param required, but you accept a None value of this param. Than you can use explicit require mark with Nont type, like below:

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
    # do sth

Default Value for Query Param

Notice, here we don't use Query(default=None, ...) to set the default value to None, but use the Python style default value. This pattern is recommended in the latest FastAPI doc, for more info please check Advantages of Annotated

Request Body

If you want to get a specified request body from a request, then you may need to declare a class inherited from pydantic.BaseModel, and use it in your function declaration, like below:

class Item(BaseModel):
    name: str
    description: str | None = None

@app.post("/items/{item_id}")
async def update_item(
    item: Item | None = None,
):
    # do sth

By doing above, FastAPI now will automatically validate the request body, and you can access the data in request body by item param.

Since the body only contains one class structure Item, it will be unwarped automatically. FastAPI will expect some data with following pattern.

{
    "name": ...,
    "description": ...,
}

Also, we could prevent this unwarp explicitly using Body(embeded=False)

Multiple Body

In case above, we only received one body object called item, we can actually add more than one body param to the function like below:

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
    # do sth

In above cases, FastAPI will know that you are expecting more than one part of body, and the JSON with pattern below is expected when you send request:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    }
}

Body with single value

Above, we add a custom class inherited from pydantic.BaseModel to indicate a param as a body param.

But like Query() and Path(), we are able to use Body() provided by FastAPI to set some extra info for a body param, or explicitly mark a param as a body param. See example below:

@app.post()
def update_item(update_if_exist: Annotated[bool, Body()]):
    # do sth

The code above explicitly set update_if_exist to be a bool value in request body, and a JSON with pattern below is expected:

{
    "update_if_exist": true
}

Notice: If you don't explicitly set the param type, then a single value will be consider as a query param.

Cookies & Headers

Using Cookie() & Header()

To get cookies and headers from a request, you can use Cookie() and Header() provided by FastAPI. Here is the code example:

from typing import Annotated

from fastapi import Cookie, FastAPI

@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
    # do sth

By add the Cookie() in the type anno, you tell FastAPI that ads_id here is a cookie param.

FastAPI will look over the received Request object's cookies dict, and auto validate the ads_id param if there has a ads_id in the cookies of this request.

The method of getting a specific header is same as you do with cookies, here we don't provide the example again.

Check Official Doc for more info.

Directly Access Request

Thing to know first, FastAPI is built with some other packages like pydantic and starlette. FastAPI allows us to access Request which provided by the Starlette package, and provide a wrap to the starlette API, check example below:

from fastapi import FastAPI, Request

@app.get("/items/{item_id}")
def read_root(item_id: str, request: Request):
    # do sth, access request param here

Cookies and Headers now can be acccessed through this request param.

Based on lastest Starlette doc, you can use request.cookies and request.headers to access them. To be simple, you can consider them as a dict.

For more info about class member in Request, check Starlette Request Reference