ποΈ FastAPI Database Integration: Building Your Data Kingdom
The Story of Your Digital Filing Cabinet
Imagine youβre running a library. You have thousands of books, and you need a way to:
- Store them so they donβt get lost
- Find them when someone asks for a specific book
- Add new ones when they arrive
- Remove old ones that are damaged
- Keep track of changes over time
Thatβs exactly what Database Integration does for your FastAPI app! Your database is like a super-organized filing cabinet that never forgets anything.
ποΈ Part 1: Database Setup with SQLAlchemy
What is SQLAlchemy?
Think of SQLAlchemy as your friendly librarian robot. Instead of you walking through dusty shelves to find books, you just tell the robot what you want, and it fetches it for you!
Without SQLAlchemy:
You: "Go to shelf 42, row 3,
find the blue book..."
With SQLAlchemy:
You: "Get me the book about cats"
Robot: "Here you go! π±"
Setting Up Your Database Connection
First, letβs tell our robot where the filing cabinet is!
Step 1: Install SQLAlchemy
pip install sqlalchemy
Step 2: Create the Connection
# database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import declarative_base
# Where is our filing cabinet?
DATABASE_URL = "sqlite:///./myapp.db"
# Create the connection to our cabinet
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False}
)
# Our robot helper for each request
SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=engine
)
# Blueprint for our filing folders
Base = declarative_base()
Understanding Each Part
graph LR A[π DATABASE_URL] --> B[π Where database lives] C[βοΈ engine] --> D[π Connection to database] E[π€ SessionLocal] --> F[π Handles conversations] G[π Base] --> H[ποΈ Template for tables]
| Component | What It Does | Real-Life Example |
|---|---|---|
DATABASE_URL |
Address of your database | Your libraryβs address |
engine |
Opens the door | Your library card |
SessionLocal |
Talks to the database | The librarian at the desk |
Base |
Blueprint for tables | How to organize shelves |
Creating Your First Table (Model)
Tables are like folders in your filing cabinet. Letβs create a folder for βUsersβ:
# models.py
from sqlalchemy import Column, Integer, String
from database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(100))
email = Column(String(100), unique=True)
age = Column(Integer)
What does this mean?
| Code | Meaning | Like⦠|
|---|---|---|
__tablename__ |
Folder name | βUsersβ label on folder |
id |
Unique number | Library card number |
primary_key |
Main identifier | Your fingerprint |
unique=True |
No duplicates | Only one you! |
Connecting to FastAPI
Now letβs introduce our robot to FastAPI:
# main.py
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from database import SessionLocal, engine
import models
# Create all the folders (tables)
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
# Get our robot helper
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
The get_db function is like:
- π Opening the filing cabinet
- π Doing your work
- π Closing it when done
π Part 2: CRUD Operations
CRUD is not a monster under your bed! Itβs your four superpowers for handling data:
graph TD C[π’ CREATE] --> CC[Add new stuff] R[π΅ READ] --> RR[Find stuff] U[π‘ UPDATE] --> UU[Change stuff] D[π΄ DELETE] --> DD[Remove stuff]
π’ CREATE - Adding New Data
Like putting a new book on the shelf:
@app.post("/users/")
def create_user(
name: str,
email: str,
db: Session = Depends(get_db)
):
# Make a new user folder
new_user = User(name=name, email=email)
# Put it in the cabinet
db.add(new_user)
# Save changes
db.commit()
# Get the updated info
db.refresh(new_user)
return new_user
Step by step:
User(...)β Create the folderdb.add()β Place it in cabinetdb.commit()β Lock in the changesdb.refresh()β Get the latest info
π΅ READ - Finding Data
Like asking the librarian for a book:
# Get ALL users
@app.get("/users/")
def get_all_users(db: Session = Depends(get_db)):
return db.query(User).all()
# Get ONE user by ID
@app.get("/users/{user_id}")
def get_user(
user_id: int,
db: Session = Depends(get_db)
):
user = db.query(User).filter(
User.id == user_id
).first()
if not user:
raise HTTPException(
status_code=404,
detail="User not found!"
)
return user
Query tricks:
| Method | What It Does |
|---|---|
.all() |
Get everything |
.first() |
Get the first match |
.filter() |
Search with conditions |
.count() |
Count how many |
π‘ UPDATE - Changing Data
Like erasing and rewriting in pencil:
@app.put("/users/{user_id}")
def update_user(
user_id: int,
name: str,
db: Session = Depends(get_db)
):
# Find the user
user = db.query(User).filter(
User.id == user_id
).first()
if not user:
raise HTTPException(
status_code=404,
detail="User not found!"
)
# Change the name
user.name = name
# Save changes
db.commit()
return {"message": "Updated!"}
π΄ DELETE - Removing Data
Like taking a book off the shelf permanently:
@app.delete("/users/{user_id}")
def delete_user(
user_id: int,
db: Session = Depends(get_db)
):
# Find the user
user = db.query(User).filter(
User.id == user_id
).first()
if not user:
raise HTTPException(
status_code=404,
detail="User not found!"
)
# Remove from cabinet
db.delete(user)
# Save changes
db.commit()
return {"message": "Deleted!"}
CRUD Summary Table
| Operation | HTTP Method | SQLAlchemy Method |
|---|---|---|
| CREATE | POST | db.add() |
| READ | GET | db.query() |
| UPDATE | PUT/PATCH | object.field = value |
| DELETE | DELETE | db.delete() |
π¦ Part 3: Database Migrations with Alembic
Why Do We Need Migrations?
Imagine your library has been running for a year. Now you want to add a βphone numberβ column to your Users folder. But you canβt just destroy everything and start over!
Migrations let you change your database structure without losing data. Itβs like renovating your house while still living in it! π
What is Alembic?
Alembic is your time-traveling construction worker. It:
- πΈ Takes snapshots of your database structure
- π§ Makes changes safely
- βͺ Can undo changes if something goes wrong
Setting Up Alembic
Step 1: Install Alembic
pip install alembic
Step 2: Initialize Alembic
alembic init alembic
This creates a magical folder structure:
π alembic/
βββ π env.py (configuration)
βββ π script.py.mako (template)
βββ π versions/ (your migrations)
Step 3: Configure Alembic
Edit alembic.ini:
# Point to your database
sqlalchemy.url = sqlite:///./myapp.db
Edit alembic/env.py:
# Import your models
from models import Base
target_metadata = Base.metadata
Creating Your First Migration
Scenario: You want to add a phone column to Users.
Step 1: Update your model
# models.py
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(100))
email = Column(String(100), unique=True)
age = Column(Integer)
phone = Column(String(20)) # NEW!
Step 2: Generate migration
alembic revision --autogenerate \
-m "add phone column"
This creates a file like:
versions/abc123_add_phone_column.py
Step 3: Apply the migration
alembic upgrade head
Understanding Migration Files
# versions/abc123_add_phone_column.py
def upgrade():
"""Go forward in time"""
op.add_column(
'users',
sa.Column('phone', sa.String(20))
)
def downgrade():
"""Go backward in time"""
op.drop_column('users', 'phone')
Essential Alembic Commands
graph LR A[alembic revision] --> B[π Create migration] C[alembic upgrade head] --> D[β¬οΈ Apply all migrations] E[alembic downgrade -1] --> F[β¬οΈ Undo last migration] G[alembic current] --> H[π Show current version] I[alembic history] --> J[π Show all migrations]
| Command | What It Does |
|---|---|
revision --autogenerate |
Auto-create migration |
upgrade head |
Apply all pending |
downgrade -1 |
Undo last one |
current |
Where am I now? |
history |
Show all migrations |
Migration Best Practices
β DO:
- Always review auto-generated migrations
- Test migrations on a copy first
- Use descriptive migration names
- Keep migrations small and focused
β DONβT:
- Edit migrations after applying them
- Delete migration files manually
- Skip migrations in production
π― Putting It All Together
Hereβs the complete flow:
graph TD A[π Define Models] --> B[ποΈ Create Tables] B --> C[π CRUD Operations] C --> D[π Need Changes?] D --> E[π¦ Alembic Migration] E --> F[β Updated Database]
Complete Example Project
π my_fastapi_project/
βββ π main.py
βββ π database.py
βββ π models.py
βββ π alembic.ini
βββ π alembic/
βββ π env.py
βββ π versions/
π Key Takeaways
- SQLAlchemy = Your friendly robot that talks to the database
- Models = Blueprints for your data folders
- Sessions = Conversations with your database
- CRUD = Your four superpowers (Create, Read, Update, Delete)
- Alembic = Your time-traveling renovation worker
Remember: Every big database started with one small table. Youβve got this! π
π Quick Reference
# Create
db.add(item)
db.commit()
# Read
db.query(Model).all()
db.query(Model).filter(Model.id == 1).first()
# Update
item.field = new_value
db.commit()
# Delete
db.delete(item)
db.commit()
Alembic Workflow:
# Change model β Generate β Apply
alembic revision --autogenerate -m "description"
alembic upgrade head
Now go build something amazing! π