FastAPI Backend with PostgreSQL: Step By Step Guide for Beginners

Build a clean, professional backend from scratch using FastAPI, PostgreSQL, and SQLAlchemy — with virtual environments, requirements, and working code you can run today.

Introduction

A backend is more than just a whole lot of code — it is what actually drives your application.
With FastAPI and PostgreSQL, you can construct an engine that is fast, scalable, and surprisingly easy to understand.

But Why FastAPI?

We are using FastAPI for our backend because it is one of the most modern and efficient Python frameworks available.
Here is why it shines:

  • High Performance: Built on Starlette and Pydantic, FastAPI is extremely fast (on par with Node.js and Go).
  • Automatic Documentation: It automatically generates interactive API documentation (Swagger UI) at /docs, saving hours of frontend coordination.
  • Type Safety: It uses standard Python type hints, which reduces bugs and improves editor support (autocomplete).
  • Async Support: It handles asynchronous code natively, which is great for database connections and heavy loads.

In this tutorial, we will explain the anatomy of a backend and give you the exact code to start with.

The High-Level Picture

Before writing code, it is important to understand where everything goes.
A great backend is like a layer cake:

  • Presentation Layer: The “Front Door” (Routes & Schemas).
  • Data Layer: The “Storage” (Database & Models).

Let’s take it layer by layer.

Step 1: Setting Up Your Development Environment

Before writing any code, you should set up a clean Python environment using a
virtual environment (venv). This isolates your project dependencies from your system Python.

Why Create a Virtual Environment?

A virtual environment is a self-contained Python workspace. It helps you:

  • Keep project dependencies separate from your system Python.
  • Avoid version conflicts between different projects.
  • Share your project easily with teammates using requirements.txt.
  • Maintain a clean, reproducible development setup.

Create a Virtual Environment

Open your terminal in the project folder and run:

Terminal
python -m venv venv
    

This creates a folder called venv with an isolated Python environment.

Activate the Virtual Environment

On macOS/Linux:

Terminal
source venv/bin/activate
    

On Windows:

Terminal
venv\Scripts\activate
    

Once activated, you should see (venv) at the beginning of your terminal prompt.

Create requirements.txt

The requirements.txt file lists all the Python packages your project needs.
Create a file named requirements.txt in your project root and add:

requirements.txt
fastapi==0.115.0
uvicorn==0.30.0
sqlalchemy==2.0.36
psycopg2-binary==2.9.10
pydantic==2.9.2
python-multipart==0.0.6

    

Note: For the most up-to-date versions, always refer to the official PyPI repository.

What Each Package Does:

  • fastapi: The web framework for building APIs.
  • uvicorn: The ASGI server that runs your FastAPI app.
  • sqlalchemy: The ORM library for database interactions.
  • psycopg2-binary: PostgreSQL adapter for Python.
  • pydantic: Data validation using Python type hints.
  • python-multipart: Handles file uploads and form data.

Install Dependencies

With your virtual environment activated, run:

Terminal
pip install -r requirements.txt
    

This installs all the packages with the specified versions.

Step 2: Setting Up the Foundation

First, we need a basic FastAPI application that acts as our server.

The Code (main.py)

main.py
from fastapi import FastAPI

app = FastAPI()

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

This creates a web server that listens for requests and responds with a simple JSON message.

Step 3: The Data Layer – Connecting to PostgreSQL

Most backends are database-driven at the heart.
We will use SQLAlchemy to easily maintain this connection.

The Database Setup (db.py)

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

# Update this with your actual database settings
DATABASE_URL = "postgresql://user:password@localhost:5432/mydb"

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
Base = declarative_base()
    

Key Concept: SessionLocal is a factory that provides a new
database session for each request, protecting our data and keeping it organized.

Step 4: Defining the Data Structure (Models & Schemas)

We need to tell Python how our data is shaped. We do this in two ways:

  • SQLAlchemy models (database layer).
  • Pydantic schemas (API layer).

The Database Model (models.py)

models.py
from sqlalchemy import Column, Integer, String
from .db import Base

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    name = Column(String)
    

The API Schema (schemas.py)

schemas.py
from pydantic import BaseModel

class UserCreate(BaseModel):
    email: str
    name: str

class UserRead(BaseModel):
    id: int
    email: str
    name: str

    class Config:
        orm_mode = True
    

Why two files?

  • User (model) represents the raw database table.
  • UserCreate and UserRead (schemas) are strict filters for data entering and leaving the API.

Step 5: The Presentation Layer – Creating the Endpoint

Now let’s bring it all together with a route.
This is where the API receives data, validates it, and saves it.

The Route Logic (main.py)

main.py
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from . import models, schemas
from .db import engine, SessionLocal

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

app = FastAPI()

# Dependency to get a DB session per request
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/users", response_model=schemas.UserRead)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = models.User(email=user.email, name=user.name)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user
    

What happens here?

  1. The client sends a JSON request with email and name.
  2. Pydantic validates the data against UserCreate.
  3. The route receives a database session via Depends(get_db).
  4. A new User is created and saved to PostgreSQL.
  5. The saved user (with its new id) is returned as a JSON response.

Step 6: Project Structure – Keeping it Clean

As your project grows, you don’t want everything in a single file.
A proper structure looks like:

Project structure
app/
├── main.py        # Starts the app and routes
├── db.py          # Database connection
├── models.py      # Database tables
└── schemas.py     # Pydantic validation

requirements.txt   # List of dependencies
.gitignore

venv/              # Virtual environment (add this folder to .gitignore)
    

Step 7: How to Run It

You will use an ASGI server called Uvicorn to run your app.

Install Uvicorn (already in requirements)

If not installed yet and you want to install manually:

Terminal
pip install uvicorn
    

Run the App

Make sure your virtual environment is activated and you are in the project root. Then run:

Terminal
uvicorn app.main:app --reload
    

What this command does:

  • app.main points to the main.py file inside the app folder.
  • :app is the FastAPI instance (app = FastAPI()).
  • --reload restarts the server automatically when you make code changes (great for development).

Check the Docs

Open your browser and go to

http://127.0.0.1:8000/docs

FastAPI automatically generates an interactive UI (Swagger UI) where you can test your API.
Try creating a user by clicking the POST /users endpoint and hitting “Try it out”.

Summary

You have now built a scalable backend foundation:

  • Created a virtual environment and installed dependencies from requirements.txt.
  • Connected PostgreSQL using SQLAlchemy.
  • Defined data with Models (database tables) and Schemas (API validation).
  • Created an API route to save users securely.
  • Set up a clean project structure that is ready to grow.
  • Ran it locally using Uvicorn and tested it in FastAPI’s interactive docs.

This setup is clean, professional, and ready for future features like authentication, error handling, and more complex business logic.

  • I’m a fullstack developer who enjoys turning ideas into smooth, user-friendly applications. I love learning new tech and sharing what I discover along the way.

CONTACT - CONTACT - CONTACT - CONTACT -