⚠️ Understanding HTTP Exceptions
In FastAPI, HTTP Exceptions allow your API to return structured error responses using proper HTTP status codes and meaningful error messages.
Instead of:
Failing silently
Returning misleading
200 OKresponsesCrashing with unhandled errors
You can raise a controlled exception that:
Stops endpoint execution
Returns a specific status code
Sends a clear message to the client
For example, if a requested book does not exist, the API should return:
404 Not Foundnot200 OK.

🔧 Importing HTTPException
To use HTTP exceptions, import HTTPException from FastAPI:
from typing import Optional
from fastapi import FastAPI, Path, Query, HTTPException 👈
from pydantic import BaseModel, FieldHTTPException gives you direct control over:
status_codedetail(error message)
🧪 Example — HTTPException in a GET Endpoint
Here’s how we raise an exception when a book does not exist:
# GET Endpoint for finding a book
@app.get("/books/{book_id}")
async def read_book(book_id: int = Path(gt=0)):
for book in BOOKS:
if book.id == book_id:
return book
raise HTTPException (status_code=404, detail="Item not found")What Happens Here?
The endpoint loops through
BOOKSIf a matching ID is found → return the book
If no match is found → execution reaches
raise HTTPExceptionFastAPI immediately stops execution
The client receives:
{
"detail": "Item not found"
}With status code:
404 Not Found🌐 Testing in the Browser
Using Swagger UI or your browser:
Send a GET request to
/books/{book_id}Provide an ID that does not exist
Instead of returning 200 OK, the API correctly returns:
404 Not Found
This ensures the response accurately reflects reality.
✏️ Using HTTP Exceptions in PUT and DELETE Requests
HTTP exceptions are equally important in update and delete operations.
🔄 PUT (Update) Method
# PUT request for updating a book.
@app.put("/books/update")
async def update_book(book: BookRequest):
book_changed = False
for i in range(len(BOOKS)):
if BOOKS[i].id == book.id:
BOOKS[i] = book
book_changed = True
break
if not book_changed:
raise HTTPException(status_code=404, detail="Item not found")How This Works
Client submits a book object
System searches for matching ID
Boolean flag tracks if update occurred
If no match is found → raise
HTTPException(404)
This prevents an invalid update from returning 200 OK.
🗑️ DELETE Method
The same pattern applies to DELETE operations:
# DELETE request.
@app.delete("/books/{book_id}")
async def delete_book(book_id: int = Path(gt=0)):
book_changed = False
for i in range(len(BOOKS)):
if BOOKS[i].id == book_id:
BOOKS.pop(i)
book_changed = True
break
if not book_changed:
raise HTTPException(status_code=404, detail="Item not found")If the book does not exist, the API responds correctly with:
404 Not Found 🌐 Browser Testing Scenarios
Try the following:
Update a book that does not exist.
Delete a book.
Attempt to delete it again.
In all cases, the API returns:
❌ Not
200 OK✅
404 — Not Found
This ensures clients are never misled.
🗂️ books2.py
from typing import Optional
from fastapi import FastAPI, Path, Query, HTTPException
from pydantic import BaseModel, Field
app = FastAPI()
class Book:
id: int
title: str
author: str
description: str
rating: int
published_date: int
def __init__(self, id, title, author, description, rating, published_date):
self.id = id
self.title = title
self.author = author
self.description = description
self.rating = rating
self.published_date = published_date
class BookRequest(BaseModel):
id: Optional[int] = Field(description="ID is not required", default=None)
title: str = Field(min_length=3)
author: str = Field(min_length=1)
description: str = Field(min_length=1, max_length=100)
rating: int = Field(gt=1, lt=6)
published_date: int = Field(gt=1999, lt=2031)
model_config = {
"json_schema_extra": {
"example": {
"title": "A new book",
"author": "Name of the author",
"description": "The description of the book",
"rating": 5,
"published_date": 2029
}
}
}
BOOKS = [
Book(1, "Computer Science Pro", "codingwithrobby", "A very nice book", 3, 2030),
Book(2, "Be Fast with FastAPI", "codingwithrobby", "A great book", 5, 2030),
Book(3, "Master Endpoints", "codingwithrobby", "An awesome book", 5, 2029),
Book(4, "HP1", "Author 1", "Book Description", 2, 2028),
Book(5, "HP2", "Author 2", "Book Description", 3, 2027),
Book(6, "HP3", "Author 3", "Book Description", 1, 2026)
]
@app.get("/books")
async def read_all_books():
return BOOKS
@app.post("/create_book")
async def create_book(book_request: BookRequest):
new_book = Book(**book_request.model_dump())
BOOKS.append(find_book_id(new_book))
@app.get("/books/{book_id}")
async def read_book(book_id: int = Path(gt=0)):
for book in BOOKS:
if book.id == book_id:
return book
raise HTTPException(status_code=404, detail="Item not found")
@app.get("/books/")
async def read_book_by_rating(book_rating: int = Query(gt=0, lt=6)):
books_to_return = []
for book in BOOKS:
if book.rating == book_rating:
books_to_return.append(book)
return books_to_return
@app.put("/books/update")
async def update_book(book: BookRequest):
book_changed = False
for i in range(len(BOOKS)):
if BOOKS[i].id == book.id:
BOOKS[i] = book
book_changed = True
break
if not book_changed:
raise HTTPException(status_code=404, detail="Item not found")
@app.delete("/books/{book_id}")
async def delete_book(book_id: int = Path(gt=0)):
book_changed = False
for i in range(len(BOOKS)):
if BOOKS[i].id == book_id:
BOOKS.pop(i)
book_changed = True <---
break
if not book_changed:
raise HTTPException(status_code=404, detail="Item not found")
@app.get("/books/publish/")
async def read_books_by_publish_date(
published_date: int = Query(gt=1999, lt=2031)):
books_to_return = []
for book in BOOKS:
if book.published_date == published_date:
books_to_return.append(book)
return books_to_return
def find_book_id(book: Book):
book.id = 1 if len(BOOKS) == 0 else BOOKS[-1].id + 1
return book