FastAPI Made Easy: A Step-by-Step Tutorial for Beginners

FastAPI Made Easy: A Step-by-Step Tutorial for Beginners

Get Started with FastAPI: Build Your First Web API with Python and Rediscover the Joy of Programming

Before we start, In case you are wondering what is FastAPI - Introduction to FastAPI

This article will discuss how to set up a project structure for a FastAPI application and build our first API in under 5 minutes after connecting to the database.

Let's start by structuring our repository. Here is the folder structure I follow for a FastAPI application. Even though you might have encountered some other kinds of structuring for FastAPI, I recommend the following as it helped me deploy my app flawlessly on different platforms.

Here is the screenshot of my folders.

Breakdown of each folder :

Folder NameWhat and Why
apiThis folder contains all the code that is required to start building an API. i.e defining the endpoint, request body, query params, etc.
crudThis folder contains all the code that, makes SQL queries on the database and fetches the data as per the request, and returns to the user as an API response
modelsAs the folder name suggests, this folder contains all the code that is responsible to identify the tables and views on the database and the columns of each table in the database. Each Model is nothing but a representation of the database Table.
schemasSchemas are nothing but user-defined types for each data object in our application. These help us to write efficient and strictly typed code
servicesThis folder contains subfolders which of each are responsible for making any logical modifications to the data fetched from cruds. The data thus modified by services are returned to the API level.
utilitiesThis contains all the utility functions that are most frequently used in our application

To talk about the rest of the files,

  • The init_db.py file is responsible to make a connection to DB and return a session every time some function that is written in this file is called.

  • The main.py file is the heart of this application, it is a commonly used file in FastAPI projects that serves as the entry point to the application. It typically contains the code that sets up and configures the FastAPI app, including defining the app instance, defining the endpoint routes and their associated functions, and running the app. (we shall discuss all these)

  • The requirements.txt file is a commonly used file in Python projects that lists the packages and their versions required for a project to run. It is often used to specify the dependencies of a Python application, making it easy for developers to recreate the same environment on different machines or servers.

Writing our First API.

  • So API creation usually happens at three hierarchical levels. The Controller (api folder) , The service levels and The Crud (crud folder) in the order specified.

Controller.

So we first write an endpoint for our API with its minimal requirements at the controller level. We can write something like this in the API file

#/api/car.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from crud import get_car_info_by_id
from database import get_db
from exceptions import CarInfoException
from schemas import Car

router = APIRouter()
@router.get("/cars/{car_id}", response_model=Car)
def get_car_info(car_id: int, session: Session = Depends(get_db)):
    try:
        car_info = get_car_info_by_id(session, car_id)
        return car_info
    except CarInfoException as cie:
        raise HTTPException(**cie.__dict__)

Breakdown of the above code :

  • We need to import necessary imports from fastAPI. APIRouter defines the set of APIs under one route. This is really helpful in documenting. Depends is fastapi's way of dependency injection, until the inputs on Depends return the true value the API processing won't move forward.

  • Based on the router we initiate we need to define the type of request and the endpoint URI.

    @router.get("/cars/{car_id}", response_model=Car)

  • Here car_id is the query param that we have to inject in url while making an API call.

  • Here response model defines what kind of response this API returns. This helps to make the code type strict.

  • Next to get the data we need to have a database session to make queries. The get_db method returns a session with every call, so it is sent as a dependency to a session variable

  • And then a call is made to crud which is already written, and that crud returns the data from the database

  • We are also expecting a custom exception and raising a FastAPI HTTPException when our exception occurs.

Service

  • Usually, a call has to go to a service method from the controller which in turn calls a crud method to get data from the database. Logics will be applied to the data if needed by the service and then returned to the controller.

  • In our case we are just querying the data and returning it to the user, so we are not writing any service method here.

Crud

#/crud/car.py
from sqlalchemy.orm import Session
from exceptions import CarInfoNotFoundError
from models import CarInfo

def get_car_info_by_id(session: Session, _id: int) -> CarInfo:
    car_info = session.query(CarInfo).get(_id)
    if car_info is None:
        raise CarInfoNotFoundError
    return car_info

Breakdown of the code :

  • This method aims to retrieve the data from the database and returns it to the controller.

  • We pass session and necessary identifiers as arguments to this method as they are needed for data retrieval.

  • CarInfo is the response type for this particular crud method. We define them in models and schemas

    car_info = session.query(CarInfo).get(_id)

  • The above line queries the Table CarInfo from the database based on the filter (_id) and returns appropriate results and store it in variable car_info.

  • This is being returned to the controller and thus is the result of the endpoint.

Making a connection to the database

  • To establish a connection to your database and your app, you need to use SQLalchemy a Python tool for database querying. Here is a basic example of the code.

  • This code is to make a connection to the PostgreSQL database. So the connection URL I use may vary with your use case, refer to the official documentation.

#/init_db.py
import os

import dotenv
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

dotenv.load_dotenv()

POSTGRES_DATABASE_URL = f'postgresql+psycopg2://{os.getenv("DB_USER")}:{os.getenv("PASSWORD")}@{os.getenv("HOST")}:{os.getenv("PORT")}/{os.getenv("NAME")}'

postgres_engine = sqlalchemy.create_engine(POSTGRES_DATABASE_URL)
PostgresSession = sessionmaker(autocommit=False, autoflush=False,bind=postgres_engine)
PostgresSession.configure(bind=postgres_engine)

Base = declarative_base()

def get_db():
    db = None
    try:
       db = PostgresSession()
       yield db
    finally:
       db.close()

The above get_db method when called returns a database session to the calling function, which can be utilized to make queries.

  • We are using the same at the controller level to add a dependent of the session before making an API call.

Instantiating the app

  • The heart of FastAPI is written in a file named main.py which is at the root level of your application. The following is the content of the main.py file

      from fastapi import FastAPI
    
      app = FastAPI()
    

To run the app and make your API work, run the following command at the root level of your app.

uvicorn is the server name. main is the name of the file where FastAPI is instantiated onto a value called app. --reload continuously watches over the changes and runs the server.

uvicorn main:app --reload
  • By default, the server will be started on port 8000. So to see the automated docs generated by FastAPI ( one of its unique features), go to the following link

    localhost:8000/docs or localhost:8000/redoc

  • Now try to hit your API from the Swagger docs itself.

I hope this helps understand the creation of API using the FastAPI framework. Great job on creating your first API using FastAPI! With FastAPI's powerful features and intuitive design, you can easily build robust and scalable web APIs in Python. By following the steps outlined in this article, you have taken the first step towards becoming a proficient FastAPI developer. Now, it's time to explore the many possibilities that FastAPI has to offer and start building your own amazing web applications. Happy coding!

Article on how to use middleware with FastAPI - Using Middleware with FastAPI

Did you find this article valuable?

Support Sai Lokesh Reddy by becoming a sponsor. Any amount is appreciated!