>_
EngineeringNotes
Back to Server Setup

FastAPI Server Guide

A comprehensive guide to building high-performance backend APIs using Python and FastAPI for Database-driven or Machine Learning applications.

What is FastAPI?

FastAPI is a modern, fast (high-performance) web framework for building APIs with Python. It is based on standard Python type hints natively, which gives you automatic validation and interactive documentation out of the box (Swagger UI).

What is PIP?

pip is the package installer for Python (similar to npm in Node.js). You use pip to install and manage software packages written in Python from the Python Package Index (PyPI).

1

Environment Setup & Dependencies

Unlike JavaScript, Python relies on Virtual Environments to isolate project dependencies. Start by choosing your preferred environment manager:

Terminal (venv)
bash
# Create project folder
mkdir fastapi-server
cd fastapi-server

# Create a virtual environment (named '.venv')
python -m venv .venv

# Activate the virtual environment
# On Windows:
.venv\Scripts\activate
# On Mac/Linux:
source .venv/bin/activate

pip install fastapi vs "fastapi[all]"

pip install fastapi

Installs only the core framework. Extremely lightweight, but requires you to manually install an ASGI server (like Uvicorn) to run your code. Best for highly customized production Docker images.

pip install "fastapi[all]"

Installs FastAPI along with standard dev tools, including `uvicorn` (the server that runs the app), `pydantic[email]` for advanced validation, and testing utilities.
This is what you want for normal development.

Install the standard dependencies to proceed:

Terminal
bash
pip install "fastapi[all]" python-dotenv

📦 Managing Dependencies (requirements.txt)

In Node.js, your dependencies are tracked automatically in package.json. In Python, the standard equivalent is the requirements.txt file. You must generate this manually so that others (or your deployment server) can install the exact same packages when cloning your project.

Saving Dependencies:

Terminal
bash
# Generate or update requirements.txt
pip freeze > requirements.txt

Installing Dependencies:

Terminal
bash
# Equivalent to 'npm install'
pip install -r requirements.txt
2

Standard Folder Structure

Unlike many opinionated frameworks, FastAPI does not enforce a specific project layout. However, a scalable and standard approach cleanly partitions responsibilities across routers, controllers (business logic), and models.

Project Structure
text
fastapi-server/
├── app/
│   ├── __init__.py
│   ├── main.py          # FastAPI application instance & entry point
│   ├── core/            # Configs, settings, security, constants
│   ├── models/          # Database models (SQLAlchemy / SQLModel / ORM)
│   ├── schemas/         # Pydantic schemas (Input/Output validation)
│   ├── api/             # Routers (Endpoints / request handling)
│   │   ├── __init__.py
│   │   ├── users.py
│   │   └── items.py
│   ├── services/        # Business logic (Controllers / database interactions)
│   └── database.py      # Database connection & session management
├── .env                 # Secret environment variables
├── .gitignore           # Git ignored files
└── requirements.txt     # 'pip freeze > requirements.txt' to track dependencies
  • api/ (Routers): Defines HTTP endpoints, parses incoming requests, and routes them to services/. Keeps routing clean.
  • services/ (Business Logic): Contains the actual logic and database operations like a controller. Never put heavy logic directly inside your route handlers!
  • models/ vs schemas/: models/ dictates how data is fundamentally stored in your SQL/NoSQL database, while schemas/ explicitly validates and types the JSON data coming IN and OUT using Pydantic.
3

Basic FastAPI Setup

Create a basic server structure in main.py. Notice how type hints (: str) are used for automatic validation and documentation.

main.py
python
from fastapi import FastAPI
from dotenv import load_dotenv
import os

# Load environment variables from a .env file
load_dotenv()

app = FastAPI(title="My Backend API")

@app.get("/")
def read_root():
    # FastAPI automatically serializes dictionaries to JSON!
    return {"message": "FastAPI Server is running!"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
    # item_id is automatically parsed and validated as an Integer
    return {"item_id": item_id, "q": q}
4

Database or ML Integration

1. Install ORM & Driver

In Python, SQLModelis highly recommended as it bridges Pydantic (data validation) and SQLAlchemy (database models). You'll also need the async Postgres driver.

Terminal
bash
pip install sqlmodel asyncpg

Update your .env file with an Async Postgres String:

.env
bash
DATABASE_URL="postgresql+asyncpg://user:password@localhost/mydb"

2. Define Models & Database Connection

database.py
python
import os
from sqlmodel import SQLModel, create_engine
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

DATABASE_URL = os.getenv("DATABASE_URL")

# Create Async Engine
engine = create_async_engine(DATABASE_URL, echo=True)

# Generate Session Dependency for Routes
async def get_session() -> AsyncSession:
    async_session = sessionmaker(
        engine, class_=AsyncSession, expire_on_commit=False
    )
    async with async_session() as session:
        yield session

# Helper to init DB tables (usually managed via Alembic migrations)
async def init_db():
    async with engine.begin() as conn:
        await conn.run_sync(SQLModel.metadata.create_all)

Create your unified Model & Schema:

models.py
python
from sqlmodel import SQLModel, Field

# This acts as BOTH a database table AND a Pydantic validation schema
class User(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    username: str = Field(index=True)
    email: str = Field(unique=True)
    age: int

3. Create User Endpoint

Inject the DB session using FastAPI's Depends functionality.

routes/users.py
python
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select
from models import User
from database import get_session

router = APIRouter(prefix="/users")

@router.post("/", response_model=User)
async def create_user(user: User, session: AsyncSession = Depends(get_session)):
    session.add(user)
    await session.commit()
    await session.refresh(user)
    return user

@router.get("/", response_model=list[User])
async def read_users(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(User))
    return result.scalars().all()
5

Run the Server

Connect everything in your main.py and start Uvicorn.

main.py
python
from fastapi import FastAPI
from contextlib import asynccontextmanager

from database import init_db
from routes import users

# Use modern lifespan for startup connections
@asynccontextmanager
async def lifespan(app: FastAPI):
    # Create DB tables if they don't exist
    await init_db()
    print('Database initialized.')
    yield
    # Cleanup logic here

app = FastAPI(lifespan=lifespan)

# Register external routers
app.include_router(users.router)

@app.get("/")
def health_check():
    return {"status": "ok", "type": "Database API"}

Start your development server using uvicorn:

Terminal
bash
# Syntax: uvicorn <filename>:<app_variable> --reload
uvicorn main:app --reload

✨ Pro-tip: Once the server runs, visit http://localhost:8000/docs to jump directly into the auto-generated Swagger UI interface. You can test your endpoints directly from your browser!