How To Use Alembic For Database Migrations in Your FastAPI Application
How To Use Alembic For Database Migrations in Your FastAPI Application
Introduction
When building a FastAPI application, managing database schema changes can be a
daunting task. As your application evolves, your database schema must adapt to
accommodate new features, bug fixes, and changing requirements.
Alembic is a lightweight and flexible database migration tool that simplifies the
process of managing database schema changes. By using Alembic, you can track
changes to your database schema, revert to previous versions, and collaborate with
your team more effectively.
In this tutorial, I will show you how to use Alembic for database migrations in a FastAPI
application by building the project from scratch. I will be using PostgreSQL as the
database server, but you should be able to follow this tutorial by using any database
server.
nashruddinamin.com
Web Development and AI - Python, React, Rust. 1
Pro Tip: Feel free to replace the virtualenv above with Pipenv or Poetry.
Note that I installed psycopg2-binary for PostgreSQL. If you are using a different
database server, you should install the corresponding database driver. For example,
install mysql-connector-python if you are using MySQL.
mkdir src
touch src/__init__.py
.
├── requirements.txt
└── src
└── __init__.py
We will put all of our database and FastAPI code in the src/ directory.
Create a new file named src/database.py and add the following code:
engine = create_engine(DATABASE_URL)
SessionLoad = sessionmaker(bind=engine)
nashruddinamin.com
Web Development and AI - Python, React, Rust. 2
Pro Tip: Make sure that you have created the database and the user on your PostgreSQL
server.
Now we write the SQLAlchemy model. Create a new file named src/models.py and add
the following code:
class Base(DeclarativeBase):
pass
class Item(Base):
__tablename__ = 'items'
After we have created the model, we want to automate the database migration using
Alembic.
The command above will create the alembic/ directory with the necessary
configuration files. After you run this command, your directory structure will look like
this:
.
├── alembic
│ ├── README
│ ├── env.py
│ ├── script.py.mako
│ └── versions
├── alembic.ini
├── requirements.txt
└── src
├── __init__.py
nashruddinamin.com
Web Development and AI - Python, React, Rust. 3
├── database.py
└── models.py
From all of the files generated by Alembic, you only need to modify the alembic/env.py
file. This file is a Python script that serves as the entry point for Alembic’s migration
process. It’s responsible for setting up the environment, loading the configuration, and
executing the migration scripts.
target_metadata = None
# alembic/env.py
from src.database import DATABASE_URL
config.set_main_option('sqlalchemy.url', DATABASE_URL)
After you run the command, you should see the “items” table is created in your
PostgreSQL database.
nashruddinamin.com
Web Development and AI - Python, React, Rust. 4
Create the API endpoints
To create the API endpoints using FastAPI, we’ll start by writing the Pydantic models.
These models are used to serialize data into JSON and perform data validation. It is
also being used in the responses where the data is converted into JSON format.
class Item(BaseModel):
id: int
name: str
price: PositiveFloat
class ItemCreate(BaseModel):
name: str
price: PositiveFloat
class ItemUpdate(BaseModel):
name: str | None = None
price: PositiveFloat | None = None
In the code above, we created 3 Pydantic models: Item will be used as a response
model for API endpoints that return a single item or a list of items, ItemCreate and
ItemUpdate will be used to ensure that data is validated and sanitized when creating
or updating items.
Now that the models are ready, we can write the API endpoints to create, read,
update, and delete the items. Open src/main.py and add the following code:
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
nashruddinamin.com
Web Development and AI - Python, React, Rust. 5
db.close()
@app.get('/items')
async def read_items(
skip: int = 0,
limit: int = 15,
db: Session = Depends(get_db)
) -> list[schemas.Item]:
"""
List all items
"""
users = db.execute(
select(models.Item)
).scalars().all()
return users
@app.post('/items')
async def create_item(
data: schemas.ItemCreate,
db: Session = Depends(get_db)
) -> schemas.Item:
"""
Create new Item
"""
item = models.Item(**data.model_dump())
db.add(item)
db.commit()
db.refresh(item)
return item
@app.get('/items/{item_id}')
async def read_item(
item_id: int,
db: Session = Depends(get_db)
) -> schemas.Item:
item = db.get(models.Item, item_id)
if item is None:
raise HTTPException(status_code=404, detail='Item not found')
return item
@app.put('/items/{item_id}')
async def update_item(
item_id: int,
data: schemas.ItemUpdate,
db: Session = Depends(get_db)
) -> schemas.Item:
"""
nashruddinamin.com
Web Development and AI - Python, React, Rust. 6
Update item
"""
item = db.get(models.Item, item_id)
if item is None:
raise HTTPException(status_code=404, detail='Item not found')
db.commit()
db.refresh(item)
return item
@app.delete('/items/{item_id}')
async def delete_item(
item_id: int,
db: Session = Depends(get_db)
) -> dict[str, str]:
"""
Delete item
"""
item = db.get(models.Item, item_id)
db.delete(item)
db.commit()
return {'message': 'Item successfully deleted'}
The code above provides the API endpoints to perform CRUD (Create, Read, Update,
Delete) operations for the items and is self-explanatory.
Pro Tip: Try the API endpoints above using Curl or HTTPie to make sure that it is working as
expected.
class Item(Base):
...
quantity: Mapped[int] = mapped_column(server_default='0')
The new line tells SQLAlchemy that the quantity column is of type int, and set the
default value to 0 if no value is provided when inserting a new row. It will also set the
value to 0 for existing rows.
nashruddinamin.com
Web Development and AI - Python, React, Rust. 7
Add the same field to the Pydantic model so the API will return the item’s quantity as
well. Open src/schemas.py and add the field:
class Item(BaseModel):
...
quantity: PositiveInt
After you run the command, the table in your PostgreSQL database should have the
“quantity” column. The field will also be returned in the API responses as well.
nashruddinamin.com
Web Development and AI - Python, React, Rust. 8