To create a Pydantic model and use it to define query parameters, you would need to use Depends() along with the parameter in your endpoint. To add description, title, etc., to query parameters, you could wrap the Query() in a Field().
It should also be noted that one could use the Literal type instead of Enum, as described here and here. Additionally, if one would like to define a List field inside a Pydantic model and use it as a query parameter, they would either need to implement this in a separate dependency class, as demonstrated here and here, or again wrap the Query() in a Field(), as shown below.
Moreover, to perform validation on query parameters inside a Pydnatic model, one could do this as usual using Pydantic's @validator, as demonstrated here, as well as here and here. Note that in this case, where the BaseModel is used for query parameters, raising ValueError would cause an Internal Server Error. Hence, you should instead raise an HTTPException when a validation fails, or use a custom exception handler, in order to handle ValueError exceptions, as shown in Option 2 of this answer. Besides @validator, one could also have additional validations for Query parameters, as described in FastAPI's documentation (see Query class implementation as well).
As a side note, regarding defining optional parameters, the example below uses the Optional type hint (accompanied by None as the default value in Query) from the typing module; however, you may also would like to have a look at this answer and this answer, which describe all the available ways on how to do that.
Working Example
from fastapi import FastAPI, Depends, Query, HTTPException
from pydantic import BaseModel, Field, validator
from typing import List, Optional, Literal
from enum import Enum
app = FastAPI()
class Status(str, Enum):
new = 'New'
old = 'Old'
class ServiceStatus(BaseModel):
status: Optional[Status] = Field (Query(None, description='Select service status'))
msg: Optional[str] = Field (Query(None, description='Type something'))
choice: Literal['a', 'b', 'c', 'd'] = Field (Query(..., description='Choose something'))
comments: List[str] = Field (Query(..., description='Add some comments'))
@validator('choice')
def check_choice(cls, v):
if v == 'b':
raise HTTPException(status_code=422, detail='Wrong choice')
return v
@app.get('/status')
def main(status: ServiceStatus = Depends()):
return status
Update 1 (regarding @validator)
Please note that in Pydantic V2, @validator has been deprecated and replaced by @field_validator. Please have a look at this answer for more details and examples.
Update 2 (regarding Query parameters)
As of FastAPI 0.115.0, one can declare Query (as well as Header and Cookie) parameters with Pydantic models in the following way:
from typing import Annotated, Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
return filter_query
ServiceStatusQueryParam. Why not just annotate thestatusparameter of your route directly withServiceStatusEnum. That will work. Do you really need an entire JSON object in a URL query parameter? Seems super cringe to me.?status={"status":"New"}or something to that effect because you set the type of thestatusquery parameter to be yourServiceStatusQueryParammodel, which in turn serializes to a JSON object. Whereas you just want your query to be?status=New, so essentially of type string, but constrained to the enum members.