FastAPI - Beginner to Pro / API Request Methods

Get All Todos from Database

🚀 Creating an API Endpoint to Fetch All Todos

Now that our database is set up and contains records, the next step is to create an API endpoint that retrieves all records from the database.

Let’s begin with the basic application setup.

🗂️ main.py

from fastapi import FastAPI
import models
from database import engine

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

🔎 What Does This Do?

models.Base.metadata.create_all(bind=engine)

This line ensures that our tables are created in the database if they do not already exist.

If todos.db does not exist, it will be created automatically.

⚠️ Important:

If you modify your models (for example, adding new columns), this command will not automatically update existing tables.

During development, it’s often easier to:

  • Delete todos.db

  • Restart the application

  • Let the tables be recreated

<aside> 💡

In future section we will learn how to enhance DB without deleting each time

</aside>


🔌 Creating a Database Dependency

To interact with the database safely, we need to create a database session dependency.

Update your imports and add the following function:

🗂️ main.py

from fastapi import FastAPI
import models
from database import SessionLocal, engine

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

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

🧠 What It Does

👉 Creates a new database session

👉 Gives it to the request handler

👉 Automatically closes it afterward

🔍 Step-by-Step Breakdown

1️⃣ Create a Session

db = SessionLocal()
  • SessionLocal is a session factory (created with sessionmaker)

  • It opens a connection to the database

  • This session is used to run queries

2️⃣ yield db

yield db
  • Sends the session to the route function

  • FastAPI uses this for dependency injection

Example usage:

@app.get("/books")
def read_books(db: Session = Depends(get_db)):
    return db.query(Book).all()

👉 The route receives the same db session

3️⃣ Always Close the Session

finally:
    db.close()
  • Runs after the request finishes (success or error)

  • Releases the database connection

  • Prevents connection leaks

⚙️ Why Use yield Instead of return?

Because yield allows FastAPI to run cleanup code afterward.

👉 Think of it as:

“Give the session → do work → clean up automatically”

✅ In Simple Terms

🧩 This function manages the full lifecycle of a database session for each request:

Open → Use → Close

✅ This is safe

✅ This is efficient

✅ This is required in most production applications

We should only open a database connection when needed and close it immediately after use.


🏗️ Creating Our First API Endpoint

Now let’s create an endpoint that fetches all records.

We’ll use dependency injection to provide the database session automatically.

🗂️ main.py

from typing import Annotated
from sqlalchemy.orm import Session
from fastapi import FastAPI, Depends
import models
from models import Todos
from database import SessionLocal, engine

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

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

@app.get("/")
async def read_all(db: Annotated[Session, Depends(get_db)]):
    return db.query(Todos).all()

🧠 What It Does

👉 Gets a database session

👉 Queries the Todos table

👉 Returns all rows

💡 What Is Dependency Injection?

Dependency injection means:

We need something (a dependency) before we execute our main function.

In this case, our endpoint depends on:

  • A database session

So FastAPI:

  • Calls get_db()

  • Opens a database session

  • Injects it into the read_all function

  • Closes it automatically afterward

All of this happens behind the scenes.

🔍 Step-by-Step Breakdown

1️⃣ db: Annotated[Session, Depends(get_db)]

This tells FastAPI to inject a database session into the function.

  • Session → SQLAlchemy database session type

  • Depends(get_db) → Calls get_db() to get the session

  • Annotated → Modern Python way to attach dependency metadata

👉 FastAPI automatically provides db when the route is called.

2️⃣ db.query(Todos)

  • Queries the database table mapped by the Todos model

  • Prepares a SELECT statement

Equivalent SQL:

SELECT * FROM todos;

3️⃣ .all()

  • Executes the query

  • Returns all matching rows as a list of objects


🖥️ Running the Application

Navigate into your project folder and run:

uvicorn main:app --reload

Then open your browser and visit:

/docs

This opens the automatically generated Swagger UI.

You will see one endpoint:

GET /

Click Execute, and all records from your database will be returned.


♻️ Improving Dependency Injection (Cleaner Code)

Writing this every time:

db: Annotated[Session, Depends(get_db)]

Can become repetitive.

Instead, we can create a reusable variable:

db_dependency = Annotated[Session, Depends(get_db)]

Then simplify our endpoint:

db_dependency = Annotated[Session, Depends(get_db)]

@app.get("/")
async def read_all(db: db_dependency):
    return db.query(Todos).all()

Now, instead of rewriting the dependency logic for every endpoint, we simply use:

db: db_dependency

Much cleaner and easier to maintain.


🧩 Why This Approach Is Powerful

With this setup:

  • Every request automatically gets a database session

  • The session closes safely after the response

  • We avoid duplicated code

  • Our API remains clean and scalable

FastAPI makes dependency injection simple by using standard Python type annotations.


🗂️ main.py

from typing import Annotated
from sqlalchemy.orm import Session
from fastapi import FastAPI, Depends
import models
from models import Todos
from database import SessionLocal, engine

app = FastAPI()

models.Base.metadata.create_all(bind=engine)

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

db_dependency = Annotated[Session, Depends(get_db)]

@app.get("/")
async def read_all(db: db_dependency):
    return db.query(Todos).all()

🫙database.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.ext.declarative import declarative_base

SQLALCHEMY_DATABASE_URL = "sqlite:///./todos.db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL,
    connect_args={"check_same_thread": False}
)

SessionLocal = sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine
)

Base = declarative_base()

💡models.py

from database import Base
from sqlalchemy import Column, Integer, String, Boolean

class Todos(Base):
    __tablename__ = "todos"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, nullable=False)
    description = Column(String)
    priority = Column(Integer)
    complete = Column(Boolean, default=False)