Query parameters
Time to expand our knowledge of API parameters and go one step further than path parameters by looking at query parameters.
Query Parameters
When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
from fastapi import FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]
The query is the set of key-value pairs that go after the ?
in a URL, separated by &
characters.
For example, in the URL:
127.0.0.1:8000/items/?skip=0&limit=10
...the query parameters are:
skip
: with a value of0
limit
: with a value of10
Both parameters come in as strings, but when you have declared them with Python types in your code (in the example above, as int
), they are automatically converted to that type and validated against it.
All the same process that applied for path parameters also applies for query parameters:
- Editor support (obviously)
- Data parsing
- Data validation
- Automatic documentation
Default values
As query parameters are not a fixed part of a path, they can be optional and can have default values.
In the example they have default values of skip=0
and limit=10
.
async def read_item(skip: int = 0, limit: int = 10):
So, going to the URL:
127.0.0.1:8000/items/
would be the same as going to:
127.0.0.1:8000/items/?skip=0&limit=10
But if you go to, for example:
127.0.0.1:8000/items/?skip=20
The parameter values in your function will be:
skip=20
: because you set it in the URLlimit=10
: because that was the default value
Optional parameters
The same way, you can declare optional query parameters:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
In this case, the function parameter q
will be optional, and as such will be None
by default. FastAPI is smart enough to notice that item_id
is a path parameter and q
is not, thus making it a query parameter.
Note
Please note that the code above only applies for Python 3.10 and above. During the course API Development, we will only see code examples compatible with Python 3.10 (older versions of Python might not support this syntax)!
When you declare a default value for non-path parameters you automatically make it optional as well. So if you don't want to add a default value but still make it optional you use | None = None
. Later on we will replace the second None
in this code by more information and validation for the parameters.
When you want to make a query parameter required, simply don't declare any default value:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
item = {"item_id": item_id, "needy": needy}
return item
Here the query parameter needy
is a required query parameter of type str
.
If you open in your browser a URL like:
http://127.0.0.1:8000/items/foo-item
...without adding the required parameter needy
, you will see an error like:
{
"detail": [
{
"loc": [
"query",
"needy"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
As needy
is a required parameter, you would need to set it in the URL:
127.0.0.1:8000/items/foo-item?needy=sooooneedy
Giving you the response:
{
"item_id": "foo-item",
"needy": "sooooneedy"
}
Other type conversions
You can also declare bool
types, and they will be converted as well:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None, short: bool = False):
item = {"item_id": item_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
In this case, if you go to:
127.0.0.1:8000/items/foo?short=1
127.0.0.1:8000/items/foo?short=True
127.0.0.1:8000/items/foo?short=true
127.0.0.1:8000/items/foo?short=on
127.0.0.1:8000/items/foo?short=yes
or any other case variation (uppercase, first letter in uppercase, etc), your function will see the parameter short
with a bool
value of True
. Otherwise as False
.
Multiple path and query parameters
You can declare multiple path parameters and query parameters at the same time, FastAPI knows which is which. And you don't have to declare them in any specific order.
They will be detected by name:
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
user_id: int, item_id: str, q: str | None = None, short: bool = False
):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item
And of course, you can define some parameters as required, some as having a default value, and some entirely optional:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(
item_id: str, needy: str, skip: int = 0, limit: int | None = None
):
item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
return item
In this case, there are 3 query parameters:
needy
, a requiredstr
.skip
, anint
with a default value of0
.limit
, an optionalint
.