MySQL
Using MySQL as the database for your FastAPI application involves setting up MySQL as the database engine and configuring SQLAlchemy to interact with MySQL. This guide will walk you through the steps to set up and use MySQL with FastAPI.
Step-by-Step Guide to Using MySQL with FastAPI
1. Install Required Packages
You will need the mysqlclient
package (or mysql-connector-python
if preferred) for connecting to MySQL and SQLAlchemy
for ORM functionality.
pip install fastapi sqlalchemy mysqlclient alembic
fastapi
: The web framework.sqlalchemy
: The ORM for database interactions.mysqlclient
: MySQL database connector.alembic
: For database migrations (optional but recommended).
Alternative: You can also use mysql-connector-python
instead of mysqlclient
:
pip install mysql-connector-python
In that case, you will use the appropriate connection URL for mysql-connector
.
2. Set Up the Project Structure
Create a project structure as follows:
fastapi_mysql/
│
├── alembic/
├── main.py
├── models.py
├── schemas.py
├── crud.py
├── database.py
├── migrations/
│ ├── env.py
│ └── versions/
└── alembic.ini
3. Database Configuration
Set up database.py
to handle the database connection and session management. You need to replace the SQLite connection string with the MySQL connection string.
# database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# Use mysqlclient (recommended)
SQLALCHEMY_DATABASE_URL = "mysql://user:password@localhost/dbname"
# Alternative: Use mysql-connector-python
# SQLALCHEMY_DATABASE_URL = "mysql+mysqlconnector://user:password@localhost/dbname"
engine = create_engine(
SQLALCHEMY_DATABASE_URL
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
- Connection URL:
mysql://user:password@localhost/dbname
formysqlclient
ormysql+mysqlconnector://user:password@localhost/dbname
formysql-connector-python
.
Replace user
, password
, localhost
, and dbname
with your MySQL credentials and database name.
4. Define SQLAlchemy Models
Create models.py
to define your database models.
# models.py
from sqlalchemy import Column, Integer, String, Boolean
from .database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
email = Column(String, unique=True, index=True)
full_name = Column(String)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
- This example defines a
User
model with several fields.
5. Pydantic Schemas
Create schemas.py
to define the data validation and serialization schemas.
# schemas.py
from pydantic import BaseModel
class UserBase(BaseModel):
username: str
email: str
full_name: str | None = None
is_active: bool | None = None
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
orm_mode = True
UserBase
: Base schema for common user fields.UserCreate
: Schema for user creation, including the password.User
: Schema for representing a user, including the ID.
6. CRUD Operations
Create crud.py
to define functions for CRUD operations.
# crud.py
from sqlalchemy.orm import Session
from . import models, schemas
def get_user(db: Session, user_id: int):
return db.query(models.User).filter(models.User.id == user_id).first()
def get_user_by_email(db: Session, email: str):
return db.query(models.User).filter(models.User.email == email).first()
def get_users(db: Session, skip: int = 0, limit: int = 10):
return db.query(models.User).offset(skip).limit(limit).all()
def create_user(db: Session, user: schemas.UserCreate):
fake_hashed_password = user.password + "notreallyhashed" # Replace with a real hashing mechanism
db_user = models.User(
username=user.username, email=user.email, full_name=user.full_name, hashed_password=fake_hashed_password
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
- These functions interact with the database using SQLAlchemy queries.
- In a real application, ensure to use a proper password hashing mechanism like
bcrypt
.
7. Create API Endpoints
In main.py
, set up the FastAPI application and endpoints.
# main.py
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from . import crud, models, schemas, database
models.Base.metadata.create_all(bind=database.engine) # Create tables
app = FastAPI()
@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(database.get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)
@app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 10, db: Session = Depends(database.get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(database.get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
/users/
: POST endpoint to create a new user./users/
: GET endpoint to list users with pagination./users/{user_id}
: GET endpoint to retrieve a user by ID.
8. Running the Application
Start your FastAPI application using Uvicorn:
uvicorn main:app --reload
- Open your browser and navigate to
http://localhost:8000/docs
to access the interactive Swagger UI. - Use the endpoints to interact with your MySQL database.
9. Handling Database Migrations with Alembic
Alembic is a lightweight database migration tool for SQLAlchemy. It's used to handle schema changes over time in a controlled and versioned manner.
Initial Alembic Setup
Initialize Alembic:
alembic init alembic
This creates the alembic
directory and configuration files.
Edit alembic.ini
to set your database connection URL:
# alembic.ini
sqlalchemy.url = mysql://user:password@localhost/dbname # Update this with your MySQL connection URL
Edit alembic/env.py
to import your metadata:
# alembic/env.py
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
from myproject.database import Base # Import your Base
# add your model's MetaData object here
# for 'autogenerate' support
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(connection=connection
, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
Generate the initial migration script:
alembic revision --autogenerate -m "Initial migration"
Apply the migration:
alembic upgrade head
This creates the initial tables in your MySQL database.
Key Concepts
- SQLAlchemy ORM: Provides a way to define your database schema as Python classes and interact with the database using Python objects.
- Pydantic: Used for defining request and response schemas and performing data validation.
- Database Sessions: Managed using SQLAlchemy's sessionmaker, allowing for controlled interactions with the database.
- CRUD Operations: Standard operations for creating, reading, updating, and deleting database records.
- Database Migrations: Alembic manages schema changes over time, enabling easy upgrades and downgrades of the database schema.
Conclusion
By following this guide, you can set up a FastAPI application with a MySQL database backend. This setup provides a robust foundation for building scalable API services with proper database interactions.
Further Reading
- FastAPI with Databases: FastAPI SQL Databases Documentation
- SQLAlchemy: SQLAlchemy Documentation
- Alembic: Alembic Documentation
- Pydantic: Pydantic Documentation
- Uvicorn: Uvicorn Documentation
These resources will help you expand your understanding and capabilities in using databases with FastAPI.