Parameter validations
FastAPI allows you to declare additional information and validation for your parameters.
Query parameter validation
To start validating query parameters you must first import Query
from fastapi
:
from fastapi import FastAPI, Query
As we have to replace the default value None
in the function with Query()
.
q: str | None = None
q: str | None = Query(default=None)
It serves the same purpose of defining that default value.
Default values & str validation
We are going to enforce that even though q
is optional, whenever it is provided, its length doesn't exceed 50 characters or is less than 3 characters.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
The same way that you can pass None
as the value for the default
parameter, you can pass other values. Let's say that you want to declare the q
query parameter to have a default value:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default="fixedquery", min_length=3, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Default value = Optional
Having a default value also makes the parameter optional.
When you need to declare a value as required while using Query
, you can simply not declare a default value.
You can also define a regular expression that the parameter should match:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str
| None = Query(default=None, min_length=3, max_length=50, regex="^fixedquery$")
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
This specific regular expression checks that the received parameter value:
^
: starts with the following characters, doesn't have characters before.fixedquery
: has the exact valuefixedquery
.$
: ends there, doesn't have any more characters afterfixedquery
.
Declare metadata
You can add more information about the parameter as metadata.
That information will be included in the generated OpenAPI and used by the documentation user interfaces and external tools.
Note
Have in mind that different tools might have different levels of OpenAPI support.
Some of them might not show all the extra information declared yet, although in most of the cases, the missing feature is already planned for development.
You can add a title
and a description
:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str
| None = Query(
default=None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
)
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
Path parameter validation
In the same way that you can declare more validations and metadata for query parameters with Query
, you can declare the same type of validations and metadata for path parameters with Path
.
First, import Path
from fastapi
:
from fastapi import FastAPI, Path
Order the parameters as you need with tricks
If you want to declare the q
query parameter without a Query
nor any default value, and the path parameter item_id
using Path
, and have them in a different order, Python has a little special syntax for that.
Pass *
, as the first parameter of the function.
Python won't do anything with that *
, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as kwargs. Even if they don't have a default value.
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Number validations
With Query
and Path
(and others you'll see later) you can declare number constraints.
Here, with ge=1
, item_id
will need to be an integer number greater than or equal to 1.
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
The same applies for:
gt
:g
reatert
hanle
:l
ess than ore
qual
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", gt=0, le=1000),
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Number validations also work for float
values.
Here's where it becomes important to be able to declare gt
and not just ge
. As with it you can require, for example, that a value must be greater than 0
, even if it is less than 1
.
So, 0.5
would be a valid value. But 0.0
or 0
would not.
And the same for lt
.
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
q: str,
size: float = Query(gt=0, lt=10.5)
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Declare metadata
You can declare all the same parameters as for Query
. For example, to declare a title
metadata value for the path parameter item_id
you can type:
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: str | None = Query(default=None),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Note
A path parameter is always required as it has to be part of the path. Even if you declared it with None
or set a default value, it would not affect anything, it would still be always required.
Request body parameter validation
The same way you can declare additional validation and metadata in path operation function parameters with Query
, Path
and Body
, you can declare validation and metadata inside of Pydantic models using Pydantic's Field
.
First, you have to import it:
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
results = {"item_id": item_id, "item": item}
return results
Note
Notice that Field
is imported directly from pydantic
, not from fastapi
as are all the rest (Query
, Path
, Body
, etc).
Declare model attribute
You can then use Field
with model attributes:
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
results = {"item_id": item_id, "item": item}
return results
Field
works the same way as Query
, Path
and Body
, it has all the same parameters, etc.
Technical Details
Actually, Query
, Path
and others you'll see next create objects of subclasses of a common Param
class, which is itself a subclass of Pydantic's FieldInfo
class.
And Pydantic's Field
returns an instance of FieldInfo
as well.
Body
also returns objects of a subclass of FieldInfo
directly. And there are others you will see later that are subclasses of the Body
class.
Remember that when you import Query
, Path
, and others from fastapi
, those are actually functions that return special classes.
Tips
Notice how each model's attribute with a type, default value and Field
has the same structure as a path operation function's parameter, with Field
instead of Path
, Query
and Body
.
Add extra information
You can declare extra information in Field
, Query
, Body
, etc. And it will be included in the generated JSON Schema.
You will learn more about adding extra information later in the docs, when learning to declare examples.
Note
Extra keys passed to Field
will also be present in the resulting OpenAPI schema for your application. As these keys may not necessarily be part of the OpenAPI specification, some OpenAPI tools, for example the OpenAPI validator, may not work with your generated schema.