# for defining optional query parameters, we use Optional
# for defining the request body, we use BaseModel
# Get request only take path and query parameter (if you put body then it'll give an error)
# ex. https://something.com/path?query=value
# here path is path parameter and value is query
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: Optional[bool] = None
@app.get("/")
def read_root():
return {'Hello': 'world'}
@app.get("/items/{item_id}")
def read_item(item_id:int, q:Optional[str]=None):
return {'item_id':item_id, 'query':q}
@app.put("/items/{item_id}")
def update_item(item_id:int, item:Item):
return {'item_name':item.name, 'item_id':item_id, 'item_price':item.price}
* like Optional, we can use from typing import Optional, List, Tuple, Set, Dict.
in the request body.
class Item(BaseModel):
name: str
price: float
info_type: List[str]
is_offer: Optional[bool] = None
--------------------------------------------------
Query -
from typing import Optional
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Optional[str] = Query(
None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3, regex="^fixedquery$", max_length=50, alias="item-query")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
So, when you need to declare a value as required while using Query, you can use ... as the first argument:
async def read_items(q: str = Query(..., min_length=3)):
---------------------------------------------------------------------
Enum
If we want to receive a valid path or query parameters then we should use Enum. In swagger, it gives the option to select path parameters
from enum import Enum
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
It works for Query parameter also but we cant use for body parameter.
-----------------------------------------------------Path:
Same as the Query parameter, we have Path parameter
if we don't want to specify any argument in a specific order, then we have to use * in the front.
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
We can add validations like gt, gr, lt, le in both Path and Query parameters.
------------------------------------------------------------------------
Body:
In the Fast API we can define two request body.
from typing import Optional
from fastapi import FastAPIfrom pydantic import BaseModel
app = FastAPI()
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None
class User(BaseModel): username: str full_name: Optional[str] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
results = {"item_id": item_id, "item": item, "user": user} return results
In this case, two request bodies we have User and Item.
so we have to provide like this
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
* we have to define body which takes only one key value then
from typing import Optional
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
class User(BaseModel):
username: str
full_name: Optional[str] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: int = Body(...)):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
We can use all validations like gt, lt, ge and le in the Body.
----------------------------------
using Body, we can add validations only for singular but how to add validation inside the body class
answer - using Field
from typing import Optional
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = Field(
None, title="The description of the item", max_length=300, example="Foo"
)
price: float = Field(..., gt=0, description="The price must be greater than zero")
tax: Optional[float] = 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
using Field, we can add validation to individual key inside class.
--------------------------------------
Nested Body:
from typing import Optional, Set
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
tags: Set[str] = set()
image: Optional[Image] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
We can add specific validations like url
from pydantic import BaseModel, HttpUrl
---------------------------------------
we can add extra data types like int, float and List
like UUID, datetime.datetime, datetime.date, datetime.time, datetime.timedelta, frozenset, bytes, Decimal
from datetime import datetime, time, timedelta
from typing import Optional
from uuid import UUID
from fastapi import Body, FastAPI
app = FastAPI()
@app.put("/items/{item_id}")
async def read_items(item_id: UUID, start_datetime: Optional[datetime] = Body(None), end_datetime: Optional[datetime] = Body(None),
repeat_at: Optional[time] = Body(None), process_after: Optional[timedelta] = Body(None)):
start_process = start_datetime + process_after
duration = end_datetime - start_process
return {"item_id": item_id, "start_datetime": start_datetime, "end_datetime": end_datetime, "repeat_at": repeat_at,
"process_after": process_after, "start_process": start_process, "duration": duration }
Here Fast API expect body like
{
"start_datetime": "2021-08-29T08:07:44.590Z",
"end_datetime": "2021-08-29T08:07:44.590Z",
"repeat_at": "string",
"process_after": 0
}
Headers:
In the Get request, we use Headers along with the Path and Query parameters.
generally, Headers are hyphen separated but since we can't write variable name with hyphen in the python hence Fast API header automatically converts underscore separated into hyphen separated.
i.e. even if we write header as header_name it converts to header-name
we want to disable it then we can do convert_underscores=False
from typing import Optional
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(strange_header: Optional[str] = Header(None, convert_underscores=False)):
return {"strange_header": strange_header}
if we want to call this api using python then
requests.get('http://127.0.0.1:8000/items/', headers={'strange-header':'header_name'}).json()
---------------------------------------------------------
Response Model:
Sometimes, we want to send users a specific type of response. here response model comes to the rescue.
suppose, we are taking email, password and other details from users and in response want to send everything except the password.
--
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Optional[str] = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user
** In the response model output, if we dont want to send default ones then we can use unset
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
** If we want to include or exclude something from the response model response
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
return items[item_id]
----------------------------------------------
Form Data:
When you need to receive form fields instead of JSON, you can use Form.
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
to call this request in the python:
requests.post('http://127.0.0.1:8000/login/', data=dict(username='username', password='password')).json()
------------------------------------------------------------------------------
read the file
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
If you declare the type of your path operation function parameter as bytes, FastAPI will read the file for you and you will receive the contents as bytes
Have in mind that this means that the whole contents will be stored in memory. This will work well for small files.
upload file:
A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk
- This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.