A hands-on crash course in FastAPI, the modern Python framework where your type hints become request validation and interactive docs for free. From your first route to running in production.
Read top to bottom. No signup, no drip — it's all here.
FastAPI is a modern Python framework for building web APIs, and its core idea is simple: the type hints you already write should do double duty. You annotate a function argument as an int or a Pydantic model, and FastAPI turns that annotation into request parsing, validation, and documentation — without a separate schema layer.
It stands on two libraries. Starlette provides the async web machinery, and Pydantic provides the data validation. Per the official documentation, the result is very high performance — on par with NodeJS and Go — with automatic, interactive API docs generated from your code.
str | None syntax used below needs Python 3.10+.Install FastAPI (the standard install bundles the server), create a file called main.py, and write a route. A route is a Python function with a decorator that maps an HTTP method and path to it.
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def read_root():
return {'Hello': 'World'}Run it in development with the built-in CLI, which uses the Uvicorn server under the hood:
fastapi dev main.py
Open http://127.0.0.1:8000 and you get the JSON back. Now open /docs and you get an interactive Swagger UI you did not write — FastAPI generated it from the OpenAPI schema it built out of your code, per the docs.
Declare a path parameter by naming it in the route string and typing it in the function signature. An argument that is not in the path and has a default value becomes a query parameter.
@app.get('/items/{item_id}')
def read_item(item_id: int, q: str | None = None):
return {'item_id': item_id, 'q': q}Here item_id is a path parameter typed as an integer — call /items/7 and your function receives the integer 7, not the string '7'. Call /items/abc and the caller gets a clear validation error automatically, because the value cannot be parsed as an int. The q argument, defaulting to None, becomes an optional query parameter, as in /items/7?q=hello. This mirrors the official first-steps example.
For anything more than a couple of fields — a POST that creates a record — you declare a Pydantic model and type a parameter with it. FastAPI reads the JSON body, validates it against the model, and hands your function a typed object.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post('/items/')
async def create_item(item: Item):
return itemThat is the whole contract. A request missing name, or sending a string where price should be a number, is rejected with a descriptive error before your function body ever runs. This example comes straight from the official request-body tutorial.
FastAPI figures out where each parameter comes from by its type and shape: names that appear in the path are path parameters, singular types with defaults are query parameters, and Pydantic models are the request body. You can use all three in one function.
@app.put('/items/{item_id}')
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {'item_id': item_id, **item.model_dump()}
if q:
result.update({'q': q})
return resultNote item.model_dump() — that is the Pydantic v2 method for turning a validated model back into a plain dictionary, shown in the tutorial. FastAPI 0.139 runs on Pydantic v2, so it is model_dump(), not the older .dict() from Pydantic v1.
You will have noticed some handlers are def and some are async def. Both work. Use async def when your handler awaits other async work — a database driver or an HTTP client that supports it — and plain def when it does not; FastAPI runs synchronous handlers in a threadpool so they never block the event loop.
When you are ready to ship, the same CLI runs the app in production mode:
fastapi run main.py
Per the official docs, the standard install includes uvicorn[standard], which pulls in high-performance pieces like uvloop. For real deployments you typically run this behind a process manager and a reverse proxy, but the application code does not change between fastapi dev and fastapi run.
Build a two-route app with / and /health, run it with fastapi dev, and confirm the interactive docs render at /docs.
Write an /echo/{count} route with count typed as int and an optional message query parameter. Test that /echo/abc returns a validation error.
Define a Pydantic Note model (title, body, optional tags), then add a POST to create and a PUT to update by id that returns model_dump().
Model a User with typed fields, then send deliberately bad input and read the automatic error response in the /docs UI to see what FastAPI rejects.
Take the Notes API, switch a handler to async def that awaits an outbound HTTP call, then run it with fastapi run behind a local reverse proxy.
Sources: FastAPI — official documentation; FastAPI — Request Body tutorial; PyPI — FastAPI release and version requirements. Grounded in the official docs and specs above — not invented API details.
The Python foundations that FastAPI builds on, from type hints to async.
The wider craft of designing, versioning, and documenting web APIs.
Wire a FastAPI backend into a full-stack, AI-powered application.
This crash course is one of the free courses at Precision AI Academy. No signup, no upsell — just the material.
See all free courses