DEV Community

John Enad
John Enad

Posted on

Day 46-47: Beginner FastAPI Series - Part 3

In this post, we'll go into a little more detail with FastAPI which I've heard is great for building APIs in Python. We'll develop a simple and straightforward API, using a 'Person' as our model with three attributes: first_name, last_name, and active. The aim is to understand and apply the techniques and concepts that I've been reading about in the last couple of days.

In part 2 we showed how routes work. This time we're actually going to work with a Person model and create functionality to persist and retrieve data from a database. For this exercise, I've chosen SQLite because its lightweight and because of it's disk-based properties. SqlLite is supposedly the most used database engine in the world because it is embedded in a lot of mobile phones and used in a lot of daily applications as well.

On a side note, I might have some future posts involving PostgreSQL and even MySQL because, why not? It sounds like a exercise. But anyway check out SQLite [here] https://www.sqlitetutorial.net/).

Our tool we're going to be using for interfacing with the SQLite database is SQLAlchemy, a SQL toolkit that provides a unified API for various relational databases. If you installed FastAPI with pip install "fastapi[all]", SQLAlchemy is already part of your setup. but if you opted for FastAPI alone, you would need to install SQLAlchemy separately with pip install sqlalchemy.

I will just point out the significant points of the code. The complete code will be in a Github repository located here: https://github.com/jenad88/enad-project-1

There is a db.py file that contains code that uses SQLAlchemy which is a SQL toolkit and Object-Relational Mapping (ORM) system for Python and sets up a connection with a SQLite database, then defines how sessions are created
and finally, it sets up a Base class for defining the database tables.

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# Create a sqlite engine instance
engine = create_engine("sqlite:///enad-core.db")

# Create SessionLocal class from sessionmaker factory
SessionLocal = sessionmaker(bind=engine, expire_on_commit=False)

# Create a DeclarativeMeta instance
Base = declarative_base()
Enter fullscreen mode Exit fullscreen mode

create_engine is used to set up the connection to the database, and Column, Integer, String, and Boolean are classes used to define the database table structures.

declarative_base is a base class used by all the models while sessionmaker is a factory that creates new Session objects.

The create_engine function initializes a new engine instance for the SQLite database which stores data in a file named enad-core.db. All connections used by SQLAlchemy comes from this engine.

SessionLocal is created from the sessionmaker factory function. I generates new Session objects when called. These objects are then used as a "handle" to the database, allowing database queries.

The declarative_base function is called to create a new Base class. This Base class serves as the base class for all SQLAlchemy models as we will show later.

The start of the application is in the /backend/app folder in a file called main.py

from fastapi import FastAPI
from backend.app.routers import person_routers_v1
from backend.app.db import Base, engine

def create_app():
    app = FastAPI()

    app.include_router(person_routers_v1.router, prefix="/api")

    return app

# Create the database
Base.metadata.create_all(engine)

app = create_app()
Enter fullscreen mode Exit fullscreen mode

The previous code creates a FastAPI application with a set of routes and sets up the SQLite database using SQLAlchemy.

Apart from fastapi being imported, the person_routers_v1 module which contain 'person' routers is also imported. Base and engine are SQLAlchemy classes used for interacting with the database.

The function create_app is a function that defines and returns a FastAPI application instance. It includes the routes from the person_routers_v1 router and has a prefix of "/api". This means that all routes defined in person_routers_v1 will have "/api" added before their route.

The Base.metadata.create_all(engine) line creates all the tables in the database from classes that inherit from Base.

app = create_app() creates an instance of the FastAPI application by calling the create_app function.

The remaining code has then been organized into three folders: models, routers and schemas

In the routers folder we have the person_routers_v1.py file. It contains the CRUD implementation (Create, Retrieve, Update, Delete) API for a 'Person' model using the FastAPI framework and uses SQLAlchemy for database communication.

from fastapi import APIRouter, Depends, HTTPException, status
from backend.app.db import SessionLocal
from sqlalchemy.orm import Session

import backend.app.models.person_models_v1 as person_models_v1
import backend.app.schemas.person_schemas_v1 as person_schemas_v1

def get_session():
    session = SessionLocal()
    try:
        yield session
    finally:
        session.close()


router = APIRouter(
    prefix="/v1/persons",
    tags=["persons"],
    responses={404: {"description": "Not found"}},
)

@router.get("", response_model=person_schemas_v1.PersonListResponse, status_code=status.HTTP_200_OK)
def get_persons(session: Session = Depends(get_session)):

    # get all persons
    persons_list = session.query(person_models_v1.Person).all()

    return {"data": persons_list}
Enter fullscreen mode Exit fullscreen mode

Note: The rest can be found in the repository: https://github.com/jenad88/enad-project-1

The get_session function establishes a connection with the database using SQLAlchemy's SessionLocal and then yields the session for the transaction. After the transaction, the session is closed.

An APIRouter router is initialized with a URL prefix and some metadata for documentation and error handling.

The router decorator is used to associate HTTP request methods (GET, POST, PUT, DELETE) with their respective functions. These functions are responsible for handling CRUD operations on the 'Person' model.

The get_persons function retrieves all Person entries from the database and returns them.

it's getting a bit long so will end here for now.

Top comments (0)