A Complete Beginner-Friendly Walkthrough notes
This guide is based on the FastAPI crash course from Telusko’s YouTube channel (1.5 hours) and walks through building a complete Product Inventory CRUD API using FastAPI, PostgreSQL, SQLAlchemy, and React.
If you’ve been wanting to start with FastAPI but don’t know where to begin, this post will help you go step by step — starting from a blank folder and ending with a fully working API connected to a real database.
📌 Video Reference: https://youtu.be/Lu8lXXlstvM?si=h13a8hQk7EWTxKR3
Git repo: https://github.com/iamharisai/Product-Inventory-Trac
What You’ll Learn
- What FastAPI is and why developers love it
- How to set up a backend project with FastAPI
- How to create API routes for CRUD operations
- Using Pydantic for clean, automatic data validation
- Wiring your API to PostgreSQL with SQLAlchemy
- Setting up a minimal React frontend to view results
- Using Swagger UI for testing your API
These notes are designed so you can follow along or come back later and quickly set everything up again.
Tools Used
- FastAPI – Backend framework
- Uvicorn – ASGI server
- React – Frontend UI
- PostgreSQL – Database
- Python – Programming language
- VS Code – Editor
What Is FastAPI?
FastAPI is a modern, high-performance Python framework built around type hints and asynchronous programming.
It works on top of:
- Starlette for request handling
- Pydantic for data validation
Why it stands out:
- Extremely fast
- Minimal boilerplate
- Auto-generated API docs at
/docs - Ideal for microservices, small apps, prototypes, and ML inference
This project is the perfect way to understand FastAPI’s workflow.
Setting Up the React Frontend
Set up the folder for this project, copy the frontend folder from git Frontend.
install dependencies:
cd frontend
npm install
npm start
This will create a folder called node_modules in your frontend directory and install packages from package.json
The React UI loads instantly. At this point, it won’t display any products yet — because our backend doesn’t serve anything. Let’s fix that.
Backend Setup
Create a virtual environment and activate it:
python3 -m venv .venv
source .venv/bin/activate
Install FastAPI and Uvicorn:
pip install fastapi uvicorn
Create a simple main.py to verify everything works:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def greet():
return "Hello, world!"
Run the server:
uvicorn main:app --reload --host 127.0.0.1 --port 8000
Now you have a working FastAPI app. Time to build real endpoints.
Creating an In-Memory Product List
Before connecting a proper database, we start with something simple: an in-memory list of products.
This helps us build and test CRUD operations without worrying about SQL or environment variables.
products = [
Product(id=1, name='Mobile', description='Motorola sleek mobile', price=250, quantity=4),
Product(id=2, name='Laptop', description='Dell Inspiron laptop', price=800, quantity=2),
Product(id=3, name='Tablet', description='Apple iPad', price=600, quantity=3),
Product(id=4, name='Smartwatch', description='Samsung Galaxy smartwatch', price=200, quantity=5)
]
But before we use this list, we need a model.
Defining Product Using Pydantic
Pydantic models define the shape of your data. FastAPI uses these models to:
- Validate incoming data
- Reject invalid requests automatically
- Serialize data into JSON
- Document your API
Create models.py:
from pydantic import BaseModel
class Product(BaseModel):
id: int
name: str
description: str
price: float
quantity: int
Now FastAPI knows exactly what a Product looks like.
CRUD Endpoints (In-Memory Version)
Get all products
@app.get("/products/")
def get_all_products():
return products
This returns the product list instantly.
Get a single product
@app.get("/products/{product_id}")
def get_product_by_id(product_id: int):
for product in products:
if product.id == product_id:
return product
return {"error": "Product not found"}
Create a product
@app.post("/products/")
def create_product(product: Product):
products.append(product)
return {"message": "Product created successfully", "product": product}
Update a product
@app.put("/products/{product_id}")
def update_product(product_id: int, product: Product):
for i in range(len(products)):
if products[i].id == product_id:
products[i] = product
return {"message": "Product updated", "product": product}
return {"error": "Product not found"}
Delete a product
@app.delete("/products/{product_id}")
def delete_product(product_id: int):
for i in range(len(products)):
if products[i].id == product_id:
del products[i]
return {"message": "Product deleted"}
return {"error": "Product not found"}
Everything works smoothly — but only in-memory.
The Limitation: No Persistence
If you restart the server:
- All newly added products disappear
- Data resets to the list we defined in the code
This is because Python stores the list in RAM. To fix this, we need a real database. Let’s wire up PostgreSQL.
Loading Environment Variables
We store DB secrets in .env and load them using pydantic-settings.
settings.py:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
DB_USER: str
DB_PASS: str
DB_HOST: str = "localhost"
DB_PORT: int = 5432
DB_NAME: str = "telusko"
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
settings = Settings()
This keeps credentials outside the code. Now install DB dependencies:
pip install sqlalchemy psycopg2
Setting Up Database Connection
Create database.py:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from settings import settings
DATABASE_URL = (
f"postgresql://{settings.DB_USER}:{settings.DB_PASS}@"
f"{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}"
)
engine = create_engine(DATABASE_URL)
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
This creates a reusable engine and database session factory.
Creating Database Tables with SQLAlchemy
from sqlalchemy import Column, Integer, Float, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Product(Base):
__tablename__ = 'product'
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
description = Column(String)
price = Column(Float)
quantity = Column(Integer)
Create the actual table:
database_models.Base.metadata.create_all(bind=engine)
Once this runs, the product table exists inside PostgreSQL.
Initializing the Database with Sample Products
To preload the initial set of products:
def get_db():
db = session()
try:
yield db
finally:
db.close()
def init_db():
db = session()
count = db.query(database_models.Product).count()
if count == 0:
for product in products:
db.add(database_models.Product(**product.model_dump()))
db.commit()
init_db()
Notice how we convert:
Pydantic model → SQLAlchemy model
using:
**product.model_dump()
This makes both models play nicely together.
Connecting backend with frontend
As frontend is using 3000 port and backend is using 8000 port, we will get blocked by CORS policy
What is CORS, or Cross-Origin Resource Sharing? is a security mechanism in web browsers that allows a web page to make requests to a domain different from its own. It works by enabling servers to send special HTTP headers that tell the browser which cross-origin requests are permitted, thereby overriding the browser’s default same-origin policy which would otherwise block such requests. This allows for more flexible web applications that can, for example, use resources from third-party APIs or fonts.
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Refactoring CRUD to Use the Database
At this point, every CRUD route must:
- Accept DB session
- Query SQLAlchemy models
- Commit changes
- Return responses
The complete version is available here:
🔗 GitHub Repo: https://github.com/iamharisai/Product-Inventory-Trac
Final Thoughts
This hands-on mini-project takes you through the essential FastAPI workflow:
- Start with a simple in-memory prototype
- Add real data validation with Pydantic
- Build CRUD operations
- Move to PostgreSQL for persistence
- Use SQLAlchemy to map models
- Connect the backend to a frontend
FastAPI makes the entire process clean, predictable, and surprisingly fast to build.
If you’re just getting started with backend development, this is a great foundation to build more real-world APIs.