FastAPI is a high-performance, async-first, type-hint safe web framework for building APIs quickly with Python.
It uses and features:
- Pydantic for data validation, parsing, and Python 3.6+ type hints.
- Starlette for HTTP requests, routing, middleware, and ASGI interface.
- Swagger (OpenAPI) for auto-generated doumentation and schemas.
- Developer ergonomics focusing on velocity and readability.
Setup and Environment
Virtual Environments
Python virtual environments isolate dependencies for individual projects, preventing conflicts.
python -m venv venv # create a virtual environment
source venv/bin/activate # activate the virtual environmentCreating a FastAPI Application
Install FastAPI:
pip install fastapi uvicornWrite your first FastAPI application:
from fastapi import FastAPI
app = FastAPI()
@app.get("/api-endpoint)
async def first_api():
return {'message': 'Hello world!'}Run the app with uvicorn:
uvicorn books:app --reload- ‘books’ is the name of the Python file, ‘app’ is the FastAPI instance.
--reloadauto-restarts the server on file change.
HTTP Methods: GET, POST, PUT, DELETE
Define endpoints with HTTP method decorators:
@app.get("/books")@app.post("/books")@app.put("/books/{book_id}"@app.delete(/books/{book_id}
Path Parameters
Path Parameters are request parameters that have been attached to the URL. You identify specific resources by utilizing a specific path.
- Anything wrapped in
{}in the path is a variable (a path parameter). - Specify the type of parameter (str, int, float, etc.) to get auto validation and docs.
- It’s important to note that order matters, the
/books/{book_id}endpoint would pattern match everything after/booksand never call themybookendpoint.
@app.get("books/{dynamic_param}")
async def read_all_books(dynamic_param):
return {'dynamic_param': dynamic_param}
@app.get("books/{book_title}")
async def read_book(book_id: int):
return get_book_by_id(book_id)Query Parameters
Query parameters are values sent in the URL after a ”?”.
e.g. GET /books/?skip=10&limit=5
- Use Optional or default to None if the parameter isn’t required.
- You can set default values using
=. /search/?keyword=magic→ Books matching ‘magic’/search/→ All books
@app.get("/search/")
async def search_books(keyword: Optional[str] = None):
if keyword:
return get_books_by_keyword(keyword)
return get_all_books()Pydantics: Data Modeling & Validation
Pydantics is a library used for data modeling and parsing.
FastAPI uses Pydantic under the hood for: request bodies (e.g., JSON data in POST or PUT), data validation, automatic error handling, generating Swagger docs.
You define a BaseModel to describe the input data. Then use that model as a param in your endpoint. The request will then be parsed into a Book object using Pydantic, automatically validating types and returning 422 Unprocessiable Entity if the data is invalid.
from pydantic import BaseModel
class Book(BaseModel):
id: int
title: str
author: str
@app.post("/books/")
async def create_book(book: Book):
return {"book": book}Field Metadata
Use Field() to add validation, constraints, and defaults or extra metadata to model attributes in FastAPI.
from pydantic import BaseModel
class Book(BaseModel):
# Value must be greater than 0.
id: int = Field(gt=0)
# Title and author must both be at least one character long.
title: str = Field(min_length=1)
author: str = Field(min_length=1)
# Description must be between 3 and 120 characters.
description: str = Field(min_length=3, max_length=120)
# An optional rating, but must be between 1 and 5 inclusive if provided.
rating: Optional[int] = Field(default=None, ge=1, le=5)You can also add description and metadata for better OpenAPI docs:
title: str = Field(..., description="Book title", example="The Hobbit")Custom Validation with @validator
You can use more complex validation using @validator:
@validator("title")
def title_must_be_capitalized(cls, v):
if not v.istitle():
raise ValueError("Title must be capitalized")
return vImmutable (Frozen) Models
You can enforce immutability with frozen=True.
class Book(BaseModel):
title: str
class Config:
frozen = True
book = Book(title="Dune")
book.title = "Dune Messiah" # ❌ raises TypeErrorParameter Validation
Path() Validation
The below code ensures that book_id is greater than 0, and gives helpful description and example in Swagger docs.
from fastapi import Path
@app.get("/books/{book_id}")
async def read_book(
book_id: int = Path(..., description="ID of book", gt=0, example=123)
):
return get_book(book_id)Query() Validation
The below code parses skip and limit as query parameters, validates types and ranges, enriches Swagger docs, and returns 422 error if validation fails.
from fastapi import Query
@app.get("/books/")
async def get_books(skip: int = Query(0, ge=0), limit: int = Query(10, le=100)):
return {"skip": skip, "limit": limit}