FastAPI Serializers and Controllers Connecting Controllers to Database

Learning objective: By the end of this lesson, students will be able to implement real CRUD operations by adding serializers and connecting the FastAPI controllers to a database.

Connecting our API

In this section, we will walk through the steps required to add serializers and connect our API to a database.

  1. Import all necessary models and schemas into each controller module.
  2. Use the correct ModelSchema for each CRUD route.
  3. Add error handling to each controller function.
  4. Test each endpoint (index, detail, create, delete, and update) using FastAPI’s built-in documentation.

Getting started

We will focus on the teas controller, which is located in the controllers/teas.py file.

1. Import necessary modules

First, let’s ensure we have all the required imports at the top of the file. Edit controllers/teas.py to look like this:


# controllers/teas.py

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from models.tea import TeaModel
from serializers.tea import TeaSchema
from typing import List
from database import get_db

router = APIRouter()

Updating the Tea controller

In this section, we will implement CRUD operations by refactoring the routes for handling GET, POST, PUT, PATCH, and DELETE HTTP methods.

Method CRUD Functionality Database Action
GET Read Retrieve data
POST Create Add new data
PUT Update Modify existing data
PATCH Update Modify existing data
DELETE Delete Remove data

1. Read/GET All Teas Route

Let’s refactor the route to get all the teas. This is the GET /teas endpoint:

@router.get("/teas", response_model=List[TeaSchema])
def get_teas(db: Session = Depends(get_db)):
    teas = db.query(TeaModel).all()
    return teas

2. Read/GET Single Tea Route

Next, let’s refactor the route to get a single tea. This is the GET /teas/{tea_id} endpoint:

@router.get("/teas/{tea_id}", response_model=TeaSchema)
def get_single_tea(tea_id: int, db: Session = Depends(get_db)):
    tea = db.query(TeaModel).filter(TeaModel.id == tea_id).first()
    if not tea:
        raise HTTPException(status_code=404, detail="Tea not found")
    return tea

3. Create/POST Route

Now, let’s refactor the POST /teas route to create a new tea in the database:

@router.post("/teas", response_model=TeaSchema)
def create_tea(tea: TeaSchema, db: Session = Depends(get_db)):
    new_tea = TeaModel(**tea.dict()) # Convert Pydantic model to SQLAlchemy model
    db.add(new_tea)
    db.commit() # Save to database
    db.refresh(new_tea) # Refresh to get the updated data (including auto-generated fields)
    return new_tea

4. Update/PUT Route

Let’s now refactor the PUT /teas/{tea_id} route to update an existing tea:

@router.put("/teas/{tea_id}", response_model=TeaSchema)
def update_tea(tea_id: int, tea: TeaSchema, db: Session = Depends(get_db)):
    db_tea = db.query(TeaModel).filter(TeaModel.id == tea_id).first()
    if not db_tea:
        raise HTTPException(status_code=404, detail="Tea not found")

    tea_data = tea.dict(exclude_unset=True)  # Only update the fields provided
    for key, value in tea_data.items():
        setattr(db_tea, key, value)

    db.commit()  # Save changes
    db.refresh(db_tea)  # Refresh to get updated data
    return db_tea

5. DELETE Route

Finally, let’s refactor the DELETE /teas/{tea_id} route to delete a tea:

@router.delete("/teas/{tea_id}")
def delete_tea(tea_id: int, db: Session = Depends(get_db)):
    db_tea = db.query(TeaModel).filter(TeaModel.id == tea_id).first()
    if not db_tea:
        raise HTTPException(status_code=404, detail="Tea not found")

    db.delete(db_tea)  # Remove from database
    db.commit()  # Save changes
    return {"message": f"Tea with ID {tea_id} has been deleted"}

Verify All Endpoints

To test the API routes, run your FastAPI application with:

pipenv run uvicorn main:app --reload

Now you can test each endpoint using FastAPI’s built-in documentation.

Navigate to FastAPI Documentation: Open http://localhost:8000/docs in your browser.