FastAPI - Beginner to Pro / API Request Methods

Get Todo by ID

βž• Adding a New Endpoint

We are going to add another endpoint to our application:

πŸ—‚οΈ main.py

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

@app.get("/todo/{todo_id}")
async def read_todo(db : db_dependency, todo_id:int):
		todo_model = db.query(Todos).filter(Todos.id == todo_id).first()

πŸ” Why .first() Is Important

The .first() method is used because our application does not know how many records in the todos table might match the provided path parameter.

As soon as a matching record is found, we want to return that data immediately instead of scanning the entire table.

Since the ID is a primary key, it should be unique and non-duplicated.


⚠️ Handling Missing Records

Before enhancing the endpoint, ensure all necessary imports are included.

πŸ—‚οΈ main.py

from typing import Annotated

from fastapi import FastAPI, Depends, HTTPException, Path  # πŸ‘ˆ Path & HTTPException added
from sqlalchemy.orm import Session
from starlette import status  # πŸ‘ˆ For HTTP status codes

import models
from models import Todos  # πŸ‘ˆ Your Todo model
from database import engine, SessionLocal

🧠 Why These Imports Matter

  • Depends β†’ Enables dependency injection

  • Session β†’ Allows interaction with the database

  • HTTPException β†’ Raises controlled errors

  • Path β†’ Adds validation to path parameters

  • status β†’ Provides readable HTTP status constants


πŸš€ Enhancing the endpoint

We now enhance the endpoint to handle cases where a todo does not exist:

πŸ—‚οΈ main.py

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

@app.get("/todo/{todo_id}")
async def read_todo(db : db_dependency, todo_id:int):
		todo_model = db.query(Todos).filter(Todos.id == todo_id).first()
		if todo_model is not None:
			return todo_model
		else:
			raise HTTPException(status_code=404, detail='Todo Not Found')

πŸ–₯️ Running the Application

Now, open your terminal and start the FastAPI server:

uvicorn main:app --reload

🌐 Testing in the Browser

Next, open your browser:

Here, you will see the available GET request endpoints:

  • /Read All

  • /todo/{todo_id} – Read Todo

Try fetching records with IDs 1, 2, and 3, and also test an ID that does not exist.


βœ… Adding a Success Status Code

Now, let’s enhance the endpoint by explicitly defining the success status code and adding validation for the path parameter:

πŸ—‚οΈ main.py

@app.get("/todo/{todo_id}", status_code=status.HTTP_200_OK)
async def read_todo(db : db_dependency, todo_id:int = Path(gt=0)):
		todo_model = db.query(Todos).filter(Todos.id == todo_id).first()
		if todo_model is not None:
			return todo_model
		else:
			raise HTTPException(status_code=4040, detail='Todo Not Found')

🧩 Adding a Status Code to the First Endpoint

We will also explicitly define the status code for the read_all endpoint:

πŸ—‚οΈ main.py

@app.get("/", status_code=status.HTTP_200_OK)
async def read_all(db: db_dependency):
	return db.query(Todos).all()

@app.get("/todo/{todo_id}", status_code=status.HTTP_200_OK)
async def read_todo(db : db_dependency, todo_id:int = Path(gt=0)):
		todo_model = db.query(Todos).filter(Todos.id == todo_id).first()
		if todo_model is not None:
			return todo_model
		else:
			raise HTTPException(status_code=404, detail='Todo Not Found')

🌐 Testing in the Browser

Let’s confirm that the status codes work as expected.

Refresh your browser. In case the server did not reload you can always start it up.

uvicorn main: app --reload

πŸ—‚οΈ main.py

from typing import Annotated
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from fastapi import FastAPI, Path, Depends, HTTPException
from starlette import status

import models
from models import Todos
from database import engine, SessionLocal

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("/", status_code=status.HTTP_200_OK)
async def read_all(db: db_dependency):
	return db.query(Todos).all()

@app.get("/todo/{todo_id}", status_code=status.HTTP_200_OK)
async def read_todo(db : db_dependency, todo_id:int = Path(gt=0)):
		todo_model = db.query(Todos).filter(Todos.id == todo_id).first()
		if todo_model is not None:
			return todo_model
		else:
			raise HTTPException(status_code=404, detail='Todo Not Found')

No Changes in models.py and database.py