Notes from https://docker.events.cube365.net/dockercon/2022/content/Videos/FznJCYerdb9Za3W9Q

Based on OpenAPI, JSON, OAuth2, generates automatic API docs.

Based on python type hints, FastAPI uses it.

class Item(BaseModel):
   name: str
 
@app.post("/items/")
async def create_item(item: Item):
    return ...

This provides autocompletion in the IDE, also JSON type checks.

from fastapi import FastAPI
 
app = FastAPI()
 
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}
 
# https://somedomain.com/items/5?q=some+query

Because q is not defined in the path, it becomes part of the query parameters.

Going to /docs will give you the Swagger interface, auto-generated.

from typing import List
from pydantic import BaseModel
 
class Food(BaseModel):
    name: str
    ingredients: List[str] = []
 
@app.post("/food/")
def prepare_food(food: Food):
    return {"message": f"preparing {food.name}"}

Because the model is not found in the path, it will be retrieved from the POST body.

@app.post("/food/")
def prepare_food(food: Food, delivery: bool = False):
    return ...

Because delivery is not found between the pydantic models, it will retrieve it from the POST query string. FastAPI will not pass a string into our methods, but rather a real bool value.

@app.post("/food/")
def prepare_food(orders: List[Food]):
    all_ingredients = []
    for food in orders:
        for ingredient in food.ingredients:
            all_ingredients.append(ingredient.lower())
 
    return {"ingredients": all_ingredients}

The types are again, considered natively and FastAPI does the magic of obtaining a JSON Array as the request body.

ingredients was defined with a default of an empty list, meaning that it will not be required. name was not, so it will be required.

Sending invalid data (this is, valid JSON but not valid to the schema defined), will return a 422 status code, and it will return detail of the validations that failed.

It will also tells us exactly where the error was (see detail[].loc)

{
  "detail": [{
    "loc": [ "body", "orders", 2, "ingredients", 1 ],
    "msg": "str type unexpected",
    "type": "type_error.str"
  }, {
    "loc": [ "body", "orders", 3, "name" ],
    "msg": "field required",
    "type": "value_error.missing"
  }]
}

Settign it up in docker:

FROM python:3.10
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

This is all that’s needed to setup a FastAPI server for it. This is what you’d normally do if you have Kubernetes or other orchestrators, or if you have a cloud service that runs the services for you.

In some cases, when you’re doing manual deployments, manual deployment commands, docker-compose or other cases, you can use this docker image:

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY ./requirements /app/requirements.tt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
COPY ./app /app

Useful if you need multiple processes inside the same container, like if you can only run a single container. It is generally advisable to use the other option.

More about it in https://fastapi.tiangolo.com/deployment/docker

Other features:

  • Dependency injection
  • OAuth2
  • Websockets
  • Files
  • Background Tasks
  • Easy GraphQL integration