FastAPI in 10 Minutes
FastAPI has emerged as a high-performance web framework for building APIs with Python, leveraging modern Python features like type hints to provide…
FastAPI has emerged as a high-performance web framework for building APIs with Python, leveraging modern Python features like type hints to provide automatic data validation, serialization, and interactive API documentation. This article details the essential steps to set up a FastAPI project, define endpoints, manage dependencies, and deploy a basic application, enabling developers to quickly build robust and efficient web services.
Setting Up Your Environment
Before diving into FastAPI development, ensure you have Python 3.7+ installed. We recommend creating a virtual environment to manage project dependencies effectively, preventing conflicts with other Python projects.
# Create a virtual environment
python3 -m venv fastapi-env
# Activate the virtual environment
# On macOS/Linux:
source fastapi-env/bin/activate
# On Windows:
.\fastapi-env\Scripts\activate
# Install FastAPI and an ASGI server (Uvicorn recommended)
pip install "fastapi[all]" uvicorn
The fastapi[all] installation pulls in common optional dependencies like Pydantic for data validation, Jinja2 for templating (though less common for pure APIs), and python-multipart for form data parsing. For most API-centric projects, pip install fastapi uvicorn is sufficient, adding specific dependencies as needed.
Your First FastAPI Application
The simplest FastAPI application defines a root endpoint that returns a JSON response. Save the following code as main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Hello, Stackvora!"}
To run this application, use Uvicorn. The --reload flag is useful for development, as it restarts the server automatically on code changes.
uvicorn main:app --reload
You can now access your API at http://127.0.0.1:8000/. FastAPI automatically provides interactive API documentation at http://127.0.0.1:8000/docs (Swagger UI) and http://127.0.0.1:8000/redoc (ReDoc).
Defining Endpoints and Path Parameters
FastAPI uses Python type hints to define the expected types of path and query parameters, which enables automatic data validation and serialization.
Path Parameters
Path parameters are defined using curly braces in the path string, similar to Flask or Django REST Framework. Type hints ensure correct data types.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
@app.get("/users/{user_name}")
async def read_user(user_name: str):
return {"user_name": user_name}
Accessing /items/5 will return {"item_id": 5}. Attempting to access /items/abc will result in a 422 Unprocessable Entity error, demonstrating FastAPI's automatic validation.
Query Parameters
Query parameters are declared as function parameters that are not part of the path. They are optional by default, but can be made mandatory or given default values.
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10, q: Optional[str] = None):
results = {"skip": skip, "limit": limit}
if q:
results.update({"q": q})
return results
/items/: Returns{"skip": 0, "limit": 10}/items/?skip=5&limit=20: Returns{"skip": 5, "limit": 20}/items/?q=fastapi_query: Returns{"skip": 0, "limit": 10, "q": "fastapi_query"}
Request Body with Pydantic
For POST, PUT, and DELETE requests, data is typically sent in the request body. FastAPI uses Pydantic models to define the structure and validation rules for this data.
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
When sending a POST request to /items/ with a JSON body like {"name": "Laptop", "price": 1200.0}, FastAPI will validate the data against the Item model, automatically handling serialization and deserialization. If the data does not conform to the model (e.g., missing name or price), FastAPI will return a 422 error with detailed validation messages.
Dependency Injection
FastAPI's dependency injection system simplifies sharing common logic, managing database connections, or handling authentication. Dependencies are callable functions (or classes) that FastAPI will execute and inject their return values into the path operation functions.
from fastapi import FastAPI, Depends, HTTPException, status
app = FastAPI()
async def get_current_user(token: str):
# In a real app, this would validate a token (e.g., JWT)
# and retrieve user information from a database.
if token != "supersecrettoken":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication token",
headers={"WWW-Authenticate": "Bearer"},
)
return {"username": "admin_user", "roles": ["admin"]}
@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
return current_user
@app.get("/admin_data")
async def read_admin_data(current_user: dict = Depends(get_current_user)):
if "admin" not in current_user.get("roles", []):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User does not have sufficient permissions",
)
return {"data": "This is sensitive admin data."}
To access /users/me, you would send a request with a header like Authorization: Bearer supersecrettoken. FastAPI automatically detects the token parameter from the Authorization header (or query parameter if not specified explicitly) and passes it to get_current_user.
Deployment Considerations
For production deployments, running Uvicorn directly is generally suitable behind a reverse proxy like Nginx or Caddy. Uvicorn supports asynchronous workers and can be scaled horizontally. A common production command:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
--host 0.0.0.0 makes the application accessible from external IPs, and --workers 4 specifies the number of Uvicorn worker processes. The optimal number of workers typically aligns with the number of CPU cores available on the server. For containerized deployments (e.g., Docker, Kubernetes), this command would be part of your container entrypoint.
Example Dockerfile
# Use an official Python runtime as a parent image
FROM python:3.9-slim-buster
# Set the working directory in the container
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy the FastAPI application code
COPY . .
# Expose port 8000 (or your chosen port)
EXPOSE 8000
# Run the Uvicorn server when the container launches
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Your requirements.txt should contain fastapi and uvicorn.
Common Pitfalls
- Missing Type Hints: FastAPI relies heavily on Python type hints for validation, serialization, and documentation. Forgetting them for path/query/body parameters will result in FastAPI treating them as plain Python objects without automatic validation or OpenAPI schema generation.
- Synchronous I/O in Async Endpoints: While FastAPI supports synchronous path operations, using blocking I/O (e.g.,
requests.get()or traditional database drivers without anawait) directly within anasync deffunction can block the entire event loop, severely degrading performance. Always useawaitwith asynchronous libraries or run blocking operations in a separate thread/process. - Incorrect Uvicorn Command: Ensure the Uvicorn command points to your FastAPI app correctly (
module_name:app_object_name, e.g.,main:appfor anappobject inmain.py). - Forgetting
uvicorn --reload: In development, not using--reloadmeans you have to manually restart Uvicorn after every code change, slowing down your feedback loop. - Security Headers in Production: While
uvicornserves fine behind a reverse proxy, ensure your proxy is configured with appropriate security headers (e.g., HSTS, Content Security Policy) and handles SSL termination. FastAPI itself doesn't directly manage these as it's typically concerned with the application layer.