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
Cookie()
& Header()
Using 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.
Request
Directly Access 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
No comment