fastapi review
FastAPI 0.141.1 is a Python ASGI framework that turns type-annotated endpoint functions into request parsing, Pydantic validation, response serialization, and an OpenAPI contract. Starlette supplies the HTTP and WebSocket layer; Pydantic handles data models. Our Python 3.12 install was pure Python, carried py.typed for type checkers, and imported successfully. The 0.141 line adds app.frontend() to coordinate a frontend during fastapi dev, while 0.141.1 fixes headers and background tasks contributed by dependencies inside that feature.
FastAPI 0.141.1 installed in 0.4 seconds, used 10 MB across 10 packages, imported in 1.30 seconds, and had no pip-audit findings in our sandbox, which makes it a low-friction base for typed Python APIs. Choose Django instead when the product needs a complete web stack, and keep blocking calls out of async def handlers.
We installed it
| Install | ✓ · 0.4s | 10 packages on disk · 10 MB |
| Import | ✓ | import fastapi in 1.30s · pure Python · py.typed · requires Python >=3.10 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does fastapi install cleanly?
Yes. In a fresh container with an empty cache, pip install fastapi finished in 0.4s, leaving 10 packages and 10 MB on disk. pip-audit reported no known vulnerabilities.
What does fastapi need to run?
Python >=3.10, and nothing compiled: it is pure Python. In our run import fastapi succeeded in 1.30s, and the package ships py.typed for type checkers.
fastapi or django: which should you use?
Pick django when the backend needs an ORM, migrations, authentication, templates, forms, and admin under one project. FastAPI 0.141.1 installed in 0.4 seconds, used 10 MB across 10 packages, imported in 1.30 seconds, and had no pip-audit findings in our sandbox, which makes it a low-friction base for typed Python APIs.
When should you not use fastapi?
You need an integrated ORM, migrations, session authentication, forms, templates, and an admin interface. Django owns that wider stack; FastAPI does not.
Discussed on
- hnFastAPI framework, high perf, easy to learn, fast to code, ready for production508 points
- hnShow HN: Faster FastAPI with simdjson and io_uring on Linux 5.19290 points
- hnPEP 563, PEP 649 and the future of pydantic and FastAPI282 points
- hnShow HN: Thinc, a new deep learning library by the makers of spaCy and FastAPI250 points
- hnSQLModel – SQL Databases in FastAPI224 points
Use it if
- A Python JSON API should derive runtime validation and OpenAPI output from the same annotated endpoint definitions.
- The service needs ASGI features such as WebSockets, streamed responses, lifespan resources, or async network clients.
- Pydantic models already describe inputs and outputs and should also control response filtering.
- The team is prepared to choose its own database layer, migration tool, authentication policy, and job queue.
- You need an integrated ORM, migrations, session authentication, forms, templates, and an admin interface. Django owns that wider stack; FastAPI does not.
- Developers will call blocking database or HTTP code directly from async def endpoints. FastAPI's docs say those handlers run on the event loop, so one blocking call stalls that worker.
- Your dependency policy requires a framework already at 1.x. FastAPI is at 0.141.1, and the Pydantic 2 transition showed that its surrounding contracts can require application changes.
- Most requests perform CPU-heavy Python work. ASGI concurrency helps while code waits for I/O, but separate processes or a job system are still needed for parallel CPU execution.
- A response-time background hook must provide persistence, retries, and cross-server execution. BackgroundTasks stays in the web process; the official guide points heavy distributed work toward a queue such as Celery.
Setup reality
We installed FastAPI 0.141.1 in a clean Python 3.12 Bookworm sandbox in 0.4 seconds. The environment ended with 10 packages using 10 MB. Our package record counted 32 direct dependencies, found pure Python code and a py.typed marker, and required Python 3.10 or newer. pip-audit found 0 known vulnerabilities. The measured license field was unknown.
Importing fastapi worked on our box and took 1.30 seconds. A plain fastapi install gives the framework core; fastapi[standard] also installs the CLI, Uvicorn, multipart parsing, templates, email validation, and settings helpers. The standard-no-fastapi-cloud-cli extra omits the cloud CLI. Use fastapi dev for reload during development and fastapi run, Uvicorn, or another ASGI process command for production.
Normal def endpoints and dependencies run in FastAPI's thread pool. async def runs on the event loop, so use awaitable clients there and move blocking calls to a sync dependency or an explicit thread. Lifespan setup creates one pool or model per worker process. BackgroundTasks starts after the response in that worker and supplies no durable handoff if the process exits.
Version 0.141 introduces app.frontend(check_dir="auto") for local frontend coordination; 0.141.1 repairs dependency-provided headers and background tasks in that path. File uploads need python-multipart. UploadFile uses a spooled file, while receiving bytes stores the complete body in memory. Multiple workers replicate application memory, and the deployment guide recommends one Uvicorn process per container for Kubernetes-style replication.
Patterns
Validate path and query values declare-route
from fastapi import FastAPI
app = FastAPI()
@app.get('/items/{item_id}')
async def read_item(item_id: int, q: str | None = None):
return {'item_id': item_id, 'query': q}FastAPI treats item_id as a required integer path value and q as an optional query parameter because only item_id appears in the route string.
Validate JSON with Pydantic parse-json-body
from pydantic import BaseModel, Field
class ItemIn(BaseModel):
name: str
quantity: int = Field(default=1, ge=1)
@app.post('/items', status_code=201)
async def create_item(item: ItemIn):
return itemA quantity below 1 is rejected before create_item runs, and the model schema becomes part of the generated OpenAPI document.
Exclude internal response fields filter-response
class UserOut(BaseModel):
id: int
email: str
@app.get('/users/{user_id}', response_model=UserOut)
def read_user(user_id: int):
return users.fetch_with_password_hash(user_id)response_model validates and filters the return value, so an extra password_hash field from storage is not serialized.
Clean up a request-scoped session inject-dependency
from typing import Annotated
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get('/orders')
def list_orders(db: Annotated[Session, Depends(get_db)]):
return db.query(Order).all()Code after yield runs as dependency cleanup. This synchronous session belongs in def code rather than an async def event-loop handler.
Raise a typed HTTP failure return-http-error
from fastapi import HTTPException
@app.get('/items/{item_id}')
def read_one(item_id: int):
item = store.get(item_id)
if item is None:
raise HTTPException(status_code=404, detail='item not found')
return itemHTTPException must be raised. Returning the object can turn an intended 404 into a normal serialized response.
Call an upstream API asynchronously await-http-client
import httpx
@app.get('/rates')
async def read_rates():
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get('https://api.example.com/rates')
response.raise_for_status()
return response.json()An async def endpoint runs on the event loop. Use an awaitable client here because requests.get() would block that worker.
Open one resource per process manage-lifespan
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.pool = await create_pool()
yield
await app.state.pool.close()
app = FastAPI(lifespan=lifespan)Lifespan runs once in each worker process. Four workers therefore create four separate pools unless the resource lives outside the application.
Read an uploaded file in chunks accept-file-upload
from fastapi import UploadFile
@app.post('/documents')
async def save_document(document: UploadFile):
while chunk := await document.read(1024 * 1024):
await object_store.write(chunk)
await document.close()
return {'filename': document.filename}UploadFile requires python-multipart and uses a spooled file. Declaring bytes instead would hold the complete upload in memory.
Run non-durable work after response schedule-small-task
from fastapi import BackgroundTasks
@app.post('/events', status_code=202)
def accept_event(event: Event, tasks: BackgroundTasks):
tasks.add_task(write_audit_copy, event.model_dump())
return {'accepted': True}BackgroundTasks executes in the web process after the response and has no persistence or retry. Use a queue when loss is unacceptable.
Replace a dependency in tests override-test-dependency
from fastapi.testclient import TestClient
from main import app, current_user
app.dependency_overrides[current_user] = lambda: {'id': 7}
client = TestClient(app)
def test_profile():
assert client.get('/profile').status_code == 200
app.dependency_overrides.clear()dependency_overrides is global to the app object. Clear it after the test so later cases do not inherit user id 7.
Send an SSE response stream-server-events
from collections.abc import AsyncIterator
from fastapi.sse import EventSourceResponse, ServerSentEvent
async def events() -> AsyncIterator[ServerSentEvent]:
yield ServerSentEvent(data='ready', event='status')
@app.get('/events')
async def event_stream():
return EventSourceResponse(events())FastAPI 0.140 added its SSE response API. Keep the generator async when it waits on queues or sockets.
Start production worker processes run-workers
fastapi run main.py --host 0.0.0.0 --port 8000 --workers 4
# Local development only:
# fastapi dev main.pyFour workers create four application processes and four copies of process memory. The official Kubernetes guidance favors one process per container.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| django | PyPI | Pick it when the backend needs an ORM, migrations, authentication, templates, forms, and admin under one project. |
| flask | PyPI | Pick it for a small synchronous WSGI service whose team wants a minimal core and mature extension conventions. |
| django-ninja | PyPI | Pick it when typed API endpoints should live inside an existing Django project and reuse Django's ORM and auth. |
| starlette | PyPI | Pick it when ASGI routing and middleware are enough and automatic Pydantic request contracts would get in the way. |
More web backend guides
urllib3 · requests · ws · anyio · undici · httpx · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

