Last modified: Feb 01, 2026 By Alexander Williams

Build a Backend REST API with Python

Building a backend REST API is a core skill for modern developers. Python makes this task straightforward and efficient. This guide will walk you through creating a fully functional API from scratch.

We will use popular tools like FastAPI and SQLAlchemy. By the end, you will have a working API that handles data securely. This is perfect for beginners starting their backend journey.

What is a REST API?

A REST API lets different software applications talk to each other over HTTP. It uses standard methods like GET, POST, PUT, and DELETE. These methods correspond to reading, creating, updating, and deleting data.

REST stands for Representational State Transfer. It is an architectural style that relies on stateless operations. This makes APIs scalable and easy to maintain.

If you are new to APIs, consider reading our Python API Tutorial for Beginners. It covers the foundational concepts in detail.

Choosing Your Python Tools

Python offers several excellent frameworks for building APIs. For this tutorial, we have selected FastAPI. It is modern, fast, and easy to learn.

FastAPI provides automatic interactive documentation. It also offers high performance, rivaling Node.js and Go. We will pair it with SQLAlchemy for database operations.

For a comparison of other options, see our guide on Python API Frameworks. It helps you choose the right tool for any project.

Project Setup and Installation

First, create a new directory for your project. Then, set up a virtual environment. This keeps your project's dependencies isolated.

Open your terminal and run the following commands.


# Create project folder
mkdir python_rest_api
cd python_rest_api

# Create and activate virtual environment (Linux/macOS)
python3 -m venv venv
source venv/bin/activate

# For Windows, use: venv\Scripts\activate

# Install required packages
pip install fastapi uvicorn sqlalchemy pydantic
    

We install four key packages. FastAPI is our web framework. Uvicorn is the ASGI server to run our app. SQLAlchemy is the ORM for the database. Pydantic is for data validation.

Defining Data Models with Pydantic

Models define the structure of your data. We use Pydantic to create these schemas. This ensures data sent to your API is valid.

Create a new file named models.py. We will define a simple model for a "Task".


from pydantic import BaseModel
from typing import Optional

# Pydantic model for data validation
class TaskCreate(BaseModel):
    title: str
    description: Optional[str] = None
    completed: bool = False

class TaskResponse(TaskCreate):
    id: int

    class Config:
        orm_mode = True
    

The TaskCreate model defines what data is needed to create a task. The TaskResponse model includes the task's ID for when we send data back. Setting orm_mode = True allows compatibility with SQLAlchemy.

Setting Up the Database with SQLAlchemy

Next, we set up the database layer. Create a file named database.py. This file will handle the database connection and table definition.


from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# Database URL (Using SQLite for simplicity)
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"

# Create the SQLAlchemy engine
engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)

# Create a SessionLocal class
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# Create a Base class
Base = declarative_base()

# Define the Task database model
class DBTask(Base):
    __tablename__ = "tasks"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    completed = Column(Boolean, default=False)

# Create all tables in the database
Base.metadata.create_all(bind=engine)
    

This code creates a connection to an SQLite database. It defines a DBTask table with columns for id, title, description, and completed status. The create_all function builds the table in the database.

Building the Core API with FastAPI

Now, let's build the main application. Create a file named main.py. This is where we define our API endpoints, or routes.


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

# Import our own modules
import models
from database import SessionLocal, DBTask

# Initialize the FastAPI app
app = FastAPI(title="Task Manager API")

# Dependency to get the database session
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# CREATE a new task
@app.post("/tasks/", response_model=models.TaskResponse)
def create_task(task: models.TaskCreate, db: Session = Depends(get_db)):
    db_task = DBTask(**task.dict())
    db.add(db_task)
    db.commit()
    db.refresh(db_task)
    return db_task

# READ all tasks
@app.get("/tasks/", response_model=List[models.TaskResponse])
def read_tasks(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
    tasks = db.query(DBTask).offset(skip).limit(limit).all()
    return tasks

# READ a single task by ID
@app.get("/tasks/{task_id}", response_model=models.TaskResponse)
def read_task(task_id: int, db: Session = Depends(get_db)):
    db_task = db.query(DBTask).filter(DBTask.id == task_id).first()
    if db_task is None:
        raise HTTPException(status_code=404, detail="Task not found")
    return db_task

# UPDATE a task
@app.put("/tasks/{task_id}", response_model=models.TaskResponse)
def update_task(task_id: int, task: models.TaskCreate, db: Session = Depends(get_db)):
    db_task = db.query(DBTask).filter(DBTask.id == task_id).first()
    if db_task is None:
        raise HTTPException(status_code=404, detail="Task not found")
    for key, value in task.dict().items():
        setattr(db_task, key, value)
    db.commit()
    db.refresh(db_task)
    return db_task

# DELETE a task
@app.delete("/tasks/{task_id}")
def delete_task(task_id: int, db: Session = Depends(get_db)):
    db_task = db.query(DBTask).filter(DBTask.id == task_id).first()
    if db_task is None:
        raise HTTPException(status_code=404, detail="Task not found")
    db.delete(db_task)
    db.commit()
    return {"message": "Task deleted successfully"}
    

This code creates a complete CRUD API. The @app.post, @app.get, @app.put, and @app.delete decorators define the endpoints. The <