Python 8 min read

FastAPI Tutorial Part 1: Introduction and Setup - Build Modern Python APIs

Start your FastAPI journey with this comprehensive guide. Learn installation, create your first API, understand async Python, and explore automatic documentation.

MR

Moshiour Rahman

Advertisement

What is FastAPI?

FastAPI is a modern, high-performance Python web framework for building APIs. Created by Sebastián Ramírez, it has become one of the most popular Python frameworks due to its speed, simplicity, and developer-friendly features.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Welcome to FastAPI!"}

That’s all you need to create a working API. But FastAPI offers much more than simplicity.

Why Choose FastAPI?

Performance Comparison

FrameworkRequests/SecondRelative Speed
FastAPI65,000+Fastest
Flask15,000Baseline
Django REST12,0000.8x
Express.js55,0000.85x

FastAPI achieves this performance through:

  • Starlette for async web handling
  • Pydantic for data validation
  • Python type hints for optimization

Key Features

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# Automatic data validation
class User(BaseModel):
    name: str
    email: str
    age: int

@app.post("/users/")
def create_user(user: User):
    # user is automatically validated
    return {"user": user, "message": "User created"}

What you get automatically:

  • Request validation
  • Response serialization
  • Interactive API documentation
  • Type checking support
  • Async/await support

Installation and Setup

Prerequisites

Ensure you have Python 3.8 or higher:

python --version
# Python 3.11.0 or higher recommended

Create Project Structure

# Create project directory
mkdir fastapi-tutorial
cd fastapi-tutorial

# Create virtual environment
python -m venv venv

# Activate virtual environment
# On macOS/Linux:
source venv/bin/activate
# On Windows:
# venv\Scripts\activate

# Install FastAPI with all dependencies
pip install "fastapi[all]"

The [all] option installs:

  • uvicorn - ASGI server
  • python-multipart - Form data support
  • jinja2 - Template engine
  • email-validator - Email validation

Project Structure

fastapi-tutorial/
├── venv/
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── routers/
│   │   └── __init__.py
│   ├── models/
│   │   └── __init__.py
│   └── schemas/
│       └── __init__.py
├── tests/
│   └── __init__.py
└── requirements.txt

Create the structure:

mkdir -p app/routers app/models app/schemas tests
touch app/__init__.py app/main.py
touch app/routers/__init__.py
touch app/models/__init__.py
touch app/schemas/__init__.py
touch tests/__init__.py

Your First FastAPI Application

Basic Application

Create app/main.py:

from fastapi import FastAPI

# Create FastAPI instance
app = FastAPI(
    title="My First API",
    description="Learning FastAPI from scratch",
    version="1.0.0"
)

@app.get("/")
def root():
    """Root endpoint returning welcome message."""
    return {"message": "Hello, FastAPI!"}

@app.get("/health")
def health_check():
    """Health check endpoint for monitoring."""
    return {"status": "healthy"}

Running the Application

# Run with uvicorn
uvicorn app.main:app --reload

# Output:
# INFO:     Uvicorn running on http://127.0.0.1:8000
# INFO:     Started reloader process
# INFO:     Started server process
# INFO:     Waiting for application startup
# INFO:     Application startup complete

Command breakdown:

  • app.main - Python module path (app/main.py)
  • app - FastAPI instance name
  • --reload - Auto-restart on code changes (development only)

Test Your API

# Using curl
curl http://localhost:8000/
# {"message":"Hello, FastAPI!"}

curl http://localhost:8000/health
# {"status":"healthy"}

Or open http://localhost:8000 in your browser.

Automatic API Documentation

FastAPI generates interactive documentation automatically.

Swagger UI

Visit http://localhost:8000/docs:

from fastapi import FastAPI

app = FastAPI(
    title="User Management API",
    description="""
    ## User Management System

    This API allows you to:
    * Create users
    * Read user information
    * Update user details
    * Delete users
    """,
    version="1.0.0",
    contact={
        "name": "API Support",
        "email": "support@example.com"
    }
)

ReDoc Documentation

Visit http://localhost:8000/redoc for alternative documentation style.

Custom Documentation

from fastapi import FastAPI

app = FastAPI(
    docs_url="/documentation",  # Change Swagger URL
    redoc_url="/redocumentation",  # Change ReDoc URL
    openapi_url="/api/openapi.json"  # Change OpenAPI schema URL
)

# Disable documentation in production
app_prod = FastAPI(docs_url=None, redoc_url=None)

HTTP Methods and Routes

GET - Retrieve Data

@app.get("/items")
def get_items():
    """Retrieve all items."""
    return {"items": ["item1", "item2", "item3"]}

@app.get("/items/{item_id}")
def get_item(item_id: int):
    """Retrieve a specific item by ID."""
    return {"item_id": item_id, "name": f"Item {item_id}"}

POST - Create Data

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

@app.post("/items")
def create_item(item: Item):
    """Create a new item."""
    return {"message": "Item created", "item": item}

PUT - Update Data

@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
    """Update an existing item."""
    return {"item_id": item_id, "updated_item": item}

DELETE - Remove Data

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    """Delete an item."""
    return {"message": f"Item {item_id} deleted"}

PATCH - Partial Update

from typing import Optional

class ItemUpdate(BaseModel):
    name: Optional[str] = None
    price: Optional[float] = None

@app.patch("/items/{item_id}")
def patch_item(item_id: int, item: ItemUpdate):
    """Partially update an item."""
    return {"item_id": item_id, "updates": item.model_dump(exclude_unset=True)}

Async Support

FastAPI supports Python’s async/await syntax for concurrent operations.

Sync vs Async

# Synchronous - blocks during I/O
@app.get("/sync")
def sync_endpoint():
    import time
    time.sleep(1)  # Blocks the entire process
    return {"type": "sync"}

# Asynchronous - non-blocking I/O
@app.get("/async")
async def async_endpoint():
    import asyncio
    await asyncio.sleep(1)  # Doesn't block other requests
    return {"type": "async"}

When to Use Async

import httpx
from fastapi import FastAPI

app = FastAPI()

# Use async for I/O operations
@app.get("/external-api")
async def call_external_api():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.example.com/data")
        return response.json()

# Use sync for CPU-bound operations
@app.get("/compute")
def compute_heavy():
    result = sum(i * i for i in range(1000000))
    return {"result": result}

Async Database Example

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

# Async database connection
DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/db"
engine = create_async_engine(DATABASE_URL)

@app.get("/users")
async def get_users():
    async with AsyncSession(engine) as session:
        result = await session.execute("SELECT * FROM users")
        return result.fetchall()

Application Configuration

Environment Variables

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "FastAPI Tutorial"
    debug: bool = False
    database_url: str
    api_key: str

    class Config:
        env_file = ".env"

settings = Settings()

app = FastAPI(title=settings.app_name, debug=settings.debug)

Create .env file:

DATABASE_URL=postgresql://user:pass@localhost/db
API_KEY=your-secret-key
DEBUG=true

Multiple Environments

from functools import lru_cache

class Settings(BaseSettings):
    environment: str = "development"

    class Config:
        env_file = ".env"

@lru_cache()
def get_settings():
    return Settings()

# Usage in endpoints
from fastapi import Depends

@app.get("/info")
def info(settings: Settings = Depends(get_settings)):
    return {
        "app_name": settings.app_name,
        "environment": settings.environment
    }

Complete Example Application

Here’s a complete starter application combining everything:

# app/main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
from datetime import datetime

app = FastAPI(
    title="Task Manager API",
    description="A simple task management API built with FastAPI",
    version="1.0.0"
)

# In-memory storage (replace with database later)
tasks_db = {}
task_counter = 0

# Models
class TaskCreate(BaseModel):
    title: str
    description: Optional[str] = None
    completed: bool = False

class Task(TaskCreate):
    id: int
    created_at: datetime

class TaskUpdate(BaseModel):
    title: Optional[str] = None
    description: Optional[str] = None
    completed: Optional[bool] = None

# Routes
@app.get("/")
def root():
    return {"message": "Task Manager API", "docs": "/docs"}

@app.get("/tasks")
def list_tasks():
    """List all tasks."""
    return {"tasks": list(tasks_db.values())}

@app.post("/tasks", status_code=201)
def create_task(task: TaskCreate):
    """Create a new task."""
    global task_counter
    task_counter += 1

    new_task = Task(
        id=task_counter,
        title=task.title,
        description=task.description,
        completed=task.completed,
        created_at=datetime.now()
    )
    tasks_db[task_counter] = new_task
    return new_task

@app.get("/tasks/{task_id}")
def get_task(task_id: int):
    """Get a specific task."""
    if task_id not in tasks_db:
        raise HTTPException(status_code=404, detail="Task not found")
    return tasks_db[task_id]

@app.put("/tasks/{task_id}")
def update_task(task_id: int, task: TaskUpdate):
    """Update a task."""
    if task_id not in tasks_db:
        raise HTTPException(status_code=404, detail="Task not found")

    stored_task = tasks_db[task_id]
    update_data = task.model_dump(exclude_unset=True)

    for field, value in update_data.items():
        setattr(stored_task, field, value)

    return stored_task

@app.delete("/tasks/{task_id}")
def delete_task(task_id: int):
    """Delete a task."""
    if task_id not in tasks_db:
        raise HTTPException(status_code=404, detail="Task not found")

    del tasks_db[task_id]
    return {"message": f"Task {task_id} deleted"}

@app.get("/tasks/stats/summary")
def task_stats():
    """Get task statistics."""
    total = len(tasks_db)
    completed = sum(1 for t in tasks_db.values() if t.completed)
    return {
        "total": total,
        "completed": completed,
        "pending": total - completed
    }

Test the Application

# Start the server
uvicorn app.main:app --reload

# Create a task
curl -X POST "http://localhost:8000/tasks" \
  -H "Content-Type: application/json" \
  -d '{"title": "Learn FastAPI", "description": "Complete the tutorial"}'

# List all tasks
curl http://localhost:8000/tasks

# Get task statistics
curl http://localhost:8000/tasks/stats/summary

Requirements File

Create requirements.txt:

fastapi[all]==0.109.0
uvicorn[standard]==0.27.0
pydantic==2.5.3
pydantic-settings==2.1.0
httpx==0.26.0

Install dependencies:

pip install -r requirements.txt

Summary

ConceptDescription
FastAPIHigh-performance Python framework
UvicornASGI server for running the app
PydanticData validation with type hints
Swagger UIInteractive API documentation at /docs
ReDocAlternative documentation at /redoc
AsyncNon-blocking I/O with async/await

Next Steps

In Part 2, we’ll dive deep into Path and Query Parameters - learning how to handle URL parameters, add validation, and create flexible API endpoints.

Series Navigation:

  • Part 1: Introduction and Setup (You are here)
  • Part 2: Path and Query Parameters
  • Part 3: Request Bodies and Pydantic
  • Part 4: Response Models and Status Codes

Advertisement

MR

Moshiour Rahman

Software Architect & AI Engineer

Share:
MR

Moshiour Rahman

Software Architect & AI Engineer

Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.

Related Articles

Comments

Comments are powered by GitHub Discussions.

Configure Giscus at giscus.app to enable comments.