Skip to main content

FastAPI Introduction

What is FastAPI?

FastAPI logo

FastAPI is a modern, high-performance web framework for building APIs with Python 3.7+ based on standard Python type hints. It was created by Sebastián Ramírez and first released in 2018. FastAPI has quickly become one of the most popular Python frameworks for API development due to its speed, ease of use, and robust feature set.

The framework was designed to be easy to use while providing high performance, automatic documentation, and strong type checking through Python's type hints system.

Why Choose FastAPI?

FastAPI offers numerous advantages that make it stand out among Python web frameworks:

  • Fast: As the name suggests, it's one of the fastest Python frameworks available, on par with NodeJS and Go.
  • Easy to Learn: Intuitive design makes it approachable for beginners.
  • Type Checking: Leverages Python's type hints to validate data and automate documentation.
  • Automatic Documentation: Generates interactive API documentation (using Swagger UI and ReDoc) automatically.
  • Based on Standards: Built on open standards like OpenAPI and JSON Schema.
  • Production Ready: Includes security, validation, and dependency injection features.

Prerequisites

Before diving into FastAPI, you should have:

  1. Python 3.7 or newer installed
  2. Basic understanding of Python programming
  3. Familiarity with HTTP requests and REST APIs (helpful but not required)

Installation

Let's start by installing FastAPI and an ASGI server called Uvicorn to run our application:

bash
pip install fastapi uvicorn

FastAPI requires an ASGI server like Uvicorn to run. ASGI (Asynchronous Server Gateway Interface) allows asynchronous Python web servers to communicate with your application.

Your First FastAPI Application

Let's create a simple "Hello World" API to understand the basics. Create a new file named main.py:

python
from fastapi import FastAPI

# Create an instance of the FastAPI class
app = FastAPI()

# Define a route with a path operation decorator
@app.get("/")
def read_root():
return {"message": "Hello World"}

To run this application, use the following command in your terminal:

bash
uvicorn main:app --reload

Let's break down this command:

  • main: the name of your Python file (main.py)
  • app: the FastAPI instance inside that file
  • --reload: automatically reloads the server when code changes (useful during development)

After running this command, your API will be available at http://127.0.0.1:8000. If you visit this URL in your browser, you'll see:

json
{"message": "Hello World"}

Automatic API Documentation

One of FastAPI's most powerful features is automatic interactive API documentation. Visit:

  • http://127.0.0.1:8000/docs - Swagger UI documentation
  • http://127.0.0.1:8000/redoc - ReDoc documentation

These pages are generated automatically from your code and allow you to explore and test your API endpoints directly from the browser.

Path Parameters

You can define path parameters in your routes using function parameters with type annotations:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"message": "Hello World"}

@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}

In this example:

  1. We've added a new route /items/{item_id} where item_id is a path parameter
  2. The parameter is typed as int, so FastAPI will convert the string from the URL to an integer
  3. If someone tries to access /items/abc, FastAPI will return an error since "abc" cannot be converted to an integer

Query Parameters

Query parameters are automatically extracted from function parameters that aren't declared in the path:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}

This endpoint can be accessed like:

  • http://127.0.0.1:8000/items/ (will use default values: skip=0, limit=10)
  • http://127.0.0.1:8000/items/?skip=20 (will use skip=20, limit=10)
  • http://127.0.0.1:8000/items/?skip=20&limit=50 (will use skip=20, limit=50)

Request Body

For POST, PUT, and other methods that need to receive data, you can define the request body using Pydantic models:

python
from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None

app = FastAPI()

@app.post("/items/")
def create_item(item: Item):
item_dict = item.dict()
if item.tax:
total_price = item.price + item.tax
item_dict.update({"total_price": total_price})
return item_dict

To test this endpoint, you can use the interactive documentation at /docs or send a POST request with JSON data:

bash
curl -X 'POST' \
'http://127.0.0.1:8000/items/' \
-H 'Content-Type: application/json' \
-d '{
"name": "Laptop",
"description": "A high-performance laptop",
"price": 999.99,
"tax": 150
}'

The response will be:

json
{
"name": "Laptop",
"description": "A high-performance laptop",
"price": 999.99,
"tax": 150,
"total_price": 1149.99
}

Real-World Example: Todo API

Let's create a simple Todo API to demonstrate FastAPI in a more practical context:

python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uuid

app = FastAPI(title="Todo API")

# Pydantic model for a Todo item
class TodoCreate(BaseModel):
title: str
description: Optional[str] = None
completed: bool = False

class Todo(TodoCreate):
id: str

# In-memory database (in a real app, you'd use a proper database)
todos = {}

@app.post("/todos/", response_model=Todo)
def create_todo(todo: TodoCreate):
# Generate a unique ID
todo_id = str(uuid.uuid4())
# Create Todo object with ID
todo_obj = Todo(id=todo_id, **todo.dict())
# Store in our "database"
todos[todo_id] = todo_obj
return todo_obj

@app.get("/todos/", response_model=List[Todo])
def read_todos(skip: int = 0, limit: int = 10):
return list(todos.values())[skip:skip+limit]

@app.get("/todos/{todo_id}", response_model=Todo)
def read_todo(todo_id: str):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
return todos[todo_id]

@app.put("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: str, todo: TodoCreate):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
updated_todo = Todo(id=todo_id, **todo.dict())
todos[todo_id] = updated_todo
return updated_todo

@app.delete("/todos/{todo_id}", response_model=Todo)
def delete_todo(todo_id: str):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
todo = todos[todo_id]
del todos[todo_id]
return todo

This API implements the basic CRUD operations (Create, Read, Update, Delete) for a Todo application. It uses:

  1. Pydantic models for request/response validation
  2. Path and query parameters
  3. HTTP methods (GET, POST, PUT, DELETE)
  4. Response models to validate and filter response data
  5. Error handling with HTTPException

Summary

In this introduction, we've covered the fundamentals of FastAPI:

  • What FastAPI is and why it's beneficial
  • Setting up a basic FastAPI application
  • Creating endpoints using path decorators
  • Working with path and query parameters
  • Handling request bodies with Pydantic models
  • Creating a real-world Todo API

FastAPI's combination of simplicity, performance, and modern features makes it an excellent choice for building APIs in Python. Its automatic documentation, data validation, and type checking help developers create robust and well-documented APIs with less code.

Additional Resources

Exercises

  1. Extend the Todo API to include a "priority" field (high, medium, low)
  2. Add an endpoint to filter todos by completion status
  3. Create a new API for a different domain (e.g., a simple blog with posts and comments)
  4. Add authentication to your API using FastAPI's security features
  5. Connect your API to a database like SQLite or PostgreSQL using SQLAlchemy

Happy coding with FastAPI!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)