FastAPI has taken the Python community by storm thanks to its speed, simplicity, and automatic documentation. If you’re new to building web services, this guide will show you how to create a fully functional RESTful API from scratch, using only plain Python and a few command‑line tools. By the end, you’ll have a local server you can query with curl or Postman, and you’ll understand the core concepts that power production‑grade APIs.
What You’ll Need
- Python 3.8+ installed on your machine
- A terminal or command prompt
- Basic knowledge of Python functions and data structures
- An internet connection to install packages
- (Optional) Docker for containerised deployment
Step 1: Set Up Your Development Environment
First, create a dedicated folder for the project. Open your terminal and run:
mkdir fastapi-tutorial && cd fastapi-tutorial It’s a good habit to use a virtual environment so that dependencies stay isolated. Run the following commands:
python -m venv venv
source venv/bin/activate # On Windows use: venvScriptsactivate When the prompt changes, you know the virtual environment is active. You can verify the Python version with python --version. If you see 3.8 or higher, you’re ready to proceed.
Step 2: Install FastAPI and Uvicorn
FastAPI is the framework, while Uvicorn is a lightning‑fast ASGI server that runs your app. Install both with pip:
pip install fastapi uvicorn[standard] The [standard] extra pulls in useful dependencies like python-multipart and email-validator. After installation, you can check the versions:
pip show fastapi uvicorn If you see version numbers, the installation succeeded.
Step 3: Create Your First Endpoint
Create a file called main.py and add the following code:
from fastapi import FastAPI
app = FastAPI()
@app.get("/ping")
async def ping():
return {"message": "pong"}
This tiny app defines a single GET endpoint at /ping that returns a JSON payload. To run it, execute:
uvicorn main:app --reload The --reload flag watches for file changes and restarts automatically—perfect for development. Open http://127.0.0.1:8000/ping in your browser; you should see {"message":"pong"}. FastAPI also generates interactive docs at /docs and /redoc—try them out.
Step 4: Add Data Validation with Pydantic
One of FastAPI’s strengths is automatic request validation using Pydantic models. Let’s build a simple /items POST endpoint that accepts a JSON body.
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.post("/items")
async def create_item(item: Item):
total_price = item.price + (item.tax or 0)
return {"name": item.name, "total_price": total_price}
When you send a request with missing or incorrectly typed fields, FastAPI automatically returns a 422 response describing the problem. Test it with curl:
curl -X POST "http://127.0.0.1:8000/items"
-H "Content-Type: application/json"
-d '{"name":"Widget","price":19.99}' The response will include the calculated total_price. If you omit price, you’ll see a clear validation error.
Step 5: Implement CRUD Operations
Real APIs need to store and retrieve data. For this tutorial we’ll use an in‑memory dictionary to keep things simple. Extend main.py as follows:
from typing import Dict
fake_db: Dict[int, Item] = {}
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id not in fake_db:
return {"error": "Item not found"}
return fake_db[item_id]
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
fake_db[item_id] = item
return {"message": "Item updated"}
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
if item_id in fake_db:
del fake_db[item_id]
return {"message": "Item deleted"}
return {"error": "Item not found"}
Now you have full Create, Read, Update, Delete (CRUD) capabilities. Use curl or Postman to hit each endpoint and observe the JSON responses. Remember, this is only for learning; a production API would use a real database like PostgreSQL.
Step 6: Add Automatic Documentation
FastAPI automatically generates OpenAPI specifications, which power the interactive Swagger UI at /docs. You can customise the metadata by passing arguments to the FastAPI() constructor:
app = FastAPI(
title="FastAPI Tutorial API",
version="1.0.0",
description="A simple example API built for a beginner’s guide."
)
Refresh /docs and you’ll see the new title and description. This documentation is invaluable for team members and external developers, as it provides live, testable endpoints without any extra effort.
Step 7: Deploy Locally with Docker (Optional)
If you want to share your API with others or test it in an isolated environment, Docker makes it painless. Create a Dockerfile in the project root:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Generate requirements.txt with:
pip freeze > requirements.txt Then build and run the container:
docker build -t fastapi-tutorial .
docker run -d -p 8000:8000 fastapi-tutorial The API is now accessible at http://localhost:8000 from any browser or client on your host machine. Docker ensures the same Python version and dependencies everywhere, reducing “it works on my machine” headaches.
Common Mistakes to Avoid
1 Forgetting to declare request bodies. If you omit the Pydantic model in a POST endpoint, FastAPI treats the payload as query parameters, leading to confusing 422 errors.
2 Using mutable default arguments. Declaring items: List[Item] = [] at function level can cause data leakage between requests. Use Depends or create a new list inside the function instead.
3 Skipping async. FastAPI supports both sync and async routes, but mixing them without care can block the event loop. Prefer async def for I/O‑bound work and keep CPU‑heavy tasks off the main thread.
4 Hard‑coding URLs. When testing locally, you might use http://127.0.0.1:8000. In production, switch to environment variables or configuration files to avoid accidental calls to the wrong host.
5 Neglecting error handling. Raising generic Exception returns a 500 response with a stack trace. Define custom exception handlers with app.exception_handler to return clean JSON error messages.
Tips and Tricks
• Leverage Dependency Injection. Use Depends to share database connections, authentication logic, or configuration across routes.
• Version your API. Prefix routes with /v1, /v2, etc., to keep backward compatibility when you add breaking changes.
• Enable CORS for frontend apps. Install fastapi[all] and add:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
adjust origins as needed.
• Use Pydantic’s Config. Set orm_mode = True when returning ORM objects, and allow_population_by_field_name = True for alias support.
• Run tests with TestClient. FastAPI ships with a built‑in client that lets you write pytest suites without a live server.
Frequently Asked Questions
Is FastAPI suitable for production?
Absolutely. Many companies run FastAPI at scale, thanks to its async support, automatic OpenAPI docs, and compatibility with high‑performance ASGI servers like Uvicorn and Hypercorn.
Do I need to learn async programming to use FastAPI?
No. FastAPI works with regular synchronous functions, but you’ll get the best performance when you use async def for I/O‑bound operations such as database calls or external API requests.
Can FastAPI work with existing Django or Flask projects?
Yes. FastAPI can be mounted as a sub‑application inside a larger ASGI stack, or you can gradually replace Flask routes with FastAPI endpoints while keeping the same project structure.
Conclusion
FastAPI lowers the barrier to building fast, standards‑compliant RESTful APIs in Python. By following this guide you’ve set up a development environment, created validated endpoints, implemented CRUD logic, explored automatic documentation, and even containerised the app with Docker. Keep experimenting—add authentication, connect a real database, and explore background tasks. The skills you’ve gained here will serve you well whether you’re building a personal project or a production microservice.





