How to Build a REST API with Python FastAPI: A Beginner’s Step-by-Step Tutorial

If you have ever wanted to build a backend without drowning in boilerplate, this FastAPI REST API tutorial is for you. FastAPI is one of the fastest growing Python frameworks in 2026, and for good reason: it is async-ready, type-safe, and ridiculously beginner friendly. By the end of this guide, you will have a working REST API with validated input and a real database connection, all in under an hour.

Unlike longer documentation walkthroughs, this tutorial focuses on shipping something that works today. No abstract theory, no 12-chapter detour. Just the practical steps.

Why FastAPI for Your First REST API?

Before we dive into the code, here is why FastAPI has become a top choice for developers building REST APIs in Python:

  • Automatic interactive docs via Swagger UI and ReDoc
  • Built-in data validation through Pydantic v2
  • Native async support for high concurrency
  • Type hints double as your API contract
  • Used in production by companies like Netflix, Uber, and Microsoft
python code laptop

What You Will Build

A small Books API with these endpoints:

Method Route Purpose
GET /books List all books
GET /books/{id} Get a single book
POST /books Create a new book
PUT /books/{id} Update a book
DELETE /books/{id} Delete a book

Step 1: Set Up Your Environment

Make sure you have Python 3.10 or higher installed. Create a project folder and a virtual environment:

mkdir books-api && cd books-api
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install fastapi uvicorn[standard] sqlalchemy pydantic

Project structure

  • main.py for the FastAPI app and routes
  • models.py for SQLAlchemy database models
  • schemas.py for Pydantic schemas
  • database.py for the DB connection

Step 2: Create the Database Connection

We will use SQLite to keep things simple. Create database.py:

from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./books.db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
python code laptop

Step 3: Define Your Database Model

In models.py:

from sqlalchemy import Column, Integer, String
from database import Base

class Book(Base):
    __tablename__ = "books"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    author = Column(String, index=True)
    year = Column(Integer)

Step 4: Add Pydantic Schemas for Validation

This is where FastAPI really shines. Pydantic validates incoming requests automatically. Create schemas.py:

from pydantic import BaseModel, Field

class BookBase(BaseModel):
    title: str = Field(..., min_length=1, max_length=200)
    author: str = Field(..., min_length=1, max_length=100)
    year: int = Field(..., ge=1450, le=2026)

class BookCreate(BookBase):
    pass

class BookOut(BookBase):
    id: int
    class Config:
        from_attributes = True

Notice how we set rules: year must be between 1450 and 2026, title cannot be empty. If a client sends invalid data, FastAPI returns a clean 422 error automatically.

Step 5: Build the Routes in main.py

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List

import models, schemas
from database import engine, get_db

models.Base.metadata.create_all(bind=engine)

app = FastAPI(title="Books API", version="1.0.0")

@app.get("/books", response_model=List[schemas.BookOut])
def list_books(db: Session = Depends(get_db)):
    return db.query(models.Book).all()

@app.get("/books/{book_id}", response_model=schemas.BookOut)
def get_book(book_id: int, db: Session = Depends(get_db)):
    book = db.query(models.Book).filter(models.Book.id == book_id).first()
    if not book:
        raise HTTPException(status_code=404, detail="Book not found")
    return book

@app.post("/books", response_model=schemas.BookOut, status_code=201)
def create_book(book: schemas.BookCreate, db: Session = Depends(get_db)):
    new_book = models.Book(**book.model_dump())
    db.add(new_book)
    db.commit()
    db.refresh(new_book)
    return new_book

@app.put("/books/{book_id}", response_model=schemas.BookOut)
def update_book(book_id: int, book: schemas.BookCreate, db: Session = Depends(get_db)):
    db_book = db.query(models.Book).filter(models.Book.id == book_id).first()
    if not db_book:
        raise HTTPException(status_code=404, detail="Book not found")
    for key, value in book.model_dump().items():
        setattr(db_book, key, value)
    db.commit()
    db.refresh(db_book)
    return db_book

@app.delete("/books/{book_id}", status_code=204)
def delete_book(book_id: int, db: Session = Depends(get_db)):
    db_book = db.query(models.Book).filter(models.Book.id == book_id).first()
    if not db_book:
        raise HTTPException(status_code=404, detail="Book not found")
    db.delete(db_book)
    db.commit()
    return None

Step 6: Run Your API

Start the server with Uvicorn:

uvicorn main:app --reload

Then open the following URLs in your browser:

  1. http://127.0.0.1:8000/docs for the Swagger UI
  2. http://127.0.0.1:8000/redoc for the ReDoc interface

You can now create, list, update, and delete books straight from the interactive docs. No Postman required.

python code laptop

Step 7: Test It With curl

curl -X POST http://127.0.0.1:8000/books \
  -H "Content-Type: application/json" \
  -d '{"title":"Clean Code","author":"Robert C. Martin","year":2008}'

You should get a 201 response with the new book and its assigned ID.

Common Pitfalls to Avoid

  • Forgetting to call db.commit() after changes
  • Using mutable default arguments in Pydantic models
  • Not closing DB sessions (use the Depends(get_db) pattern shown above)
  • Returning SQLAlchemy objects without a response_model in production

Next Steps to Level Up

Once your first API is running, here is what to tackle next:

  1. Add JWT authentication with python-jose
  2. Switch SQLite for PostgreSQL in production
  3. Add Alembic migrations for schema versioning
  4. Write tests using pytest and the FastAPI TestClient
  5. Deploy with Docker and a process manager like Gunicorn + Uvicorn workers

FAQ

Can we build a REST API using FastAPI?

Yes. FastAPI is purpose-built for REST APIs. It handles routing, validation, serialization, and OpenAPI documentation out of the box.

Is FastAPI a REST API framework?

FastAPI is a web framework that excels at building REST APIs, but it can also serve GraphQL, WebSockets, and async background tasks.

Is FastAPI beginner friendly?

Absolutely. Thanks to Python type hints, automatic docs, and clear error messages, most beginners can ship a working API in less than an hour, as this tutorial demonstrates.

Is FastAPI used in production by big companies?

Yes. Netflix, Uber, Microsoft, and many fintech startups use FastAPI in production for internal tools and customer-facing APIs.

Do I need to know async Python to use FastAPI?

No. You can write standard synchronous route functions (as we did in this tutorial). Async is a bonus when you need higher throughput.

Wrapping Up

You now have a complete FastAPI REST API with database persistence, input validation, and interactive documentation. The same patterns scale up to large production systems. Bookmark this tutorial, fork the code, and start building.

Happy shipping from the team at adproductstogo.com.