๐ Creating an API Endpoint to Retrieve a Book by ID
In this section, we are going to build an endpoint that allows a client to retrieve a single book using its unique identifier.
This introduces a very important backend concept:
Accessing a specific resource using a Path Parameter.
๐ Define the GET Endpoint
Here is the implementation:
@app.get("/books/{book_id}") <--- # ๐ path parameter
async def read_book(book_id: int):
for book in BOOKS:
if book.id == book_id:
return bookNow letโs break this down properly ๐
๐งญ Understanding the Route Path
"/books/{book_id}"๐ {book_id} is a Path Parameter โ its value comes from the URL.
Example:
/books/3FastAPI interprets this as:
book_id = 3
โ๏ธ What Happens Behind the Scenes
When a request is made to /books/3:
FastAPI matches
/books/{book_id}Extracts
3from the URLValidates it as an
int(sincebook_id: int)Calls the function with
book_id=3
๐ If the value isnโt a valid integer, FastAPI returns a 422 validation error automatically.
Important: Validation happens before your function runs.
๐ฆ Understanding the Python Function Logic
for book in BOOKS:
if book.id == book_id:
return bookHereโs whatโs happening:
We iterate through the in-memory
BOOKSlist.We compare each bookโs
idwith the requestedbook_id.If a match is found โ we return the book.
๐ง Backend Concept
This is simulating what a database query would do.
In a real production backend, this would look like:
SELECT * FROM books WHERE id = ?
# ๐ We will cover this later in the courseRight now, weโre using a list โ but conceptually, this is a resource lookup operation.
โ Important Observation
What happens if the book does not exist?
Currently:
The function returns nothing.
FastAPI returns
None.
In a production-grade backend, we would raise an HTTP exception (weโll improve this later).
For now, focus on understanding:
This endpoint retrieves exactly one resource based on identity.
๐ Testing the Endpoint in Swagger
Now letโs verify our endpoint.
๐ฅ Open your browser
๐ Navigate to:
<http://127.0.0.1:8000/docs>Locate the GET /books/{book_id} endpoint.
Click on it.
Click Try it out.
Enter a valid book ID.
Click Execute.
If the book exists:
โ You should see the correct book returned.
๐๏ธ books2.py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Book:
id: int
title: str
author: str
description: str
rating: int
def __init__(self, id, title, author, description, rating):
self.id = id
self.title = title
self.author = author
self.description = description
self.rating = rating
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=0, lt=6)
model_config = {
"json_schema_extra": {
"example": {
"title": "A new book",
"author": "Name of the author",
"description": "The description of the book",
"rating": 5
}
}
}
BOOKS = [
Book(1, 'Computer Science Pro', 'codingwithrobby', 'A very nice book', 3),
Book(2, 'Be Fast with FastAPI', 'codingwithrobby', 'A great book', 5),
Book(3, 'Master Endpoints', 'codingwithrobby', 'An awesome book', 5),
Book(4, 'HP1', 'Author 1', 'Book Description', 2),
Book(5, 'HP2', 'Author 2', 'Book Description', 3),
Book(6, 'HP3', 'Author 3', 'Book Description', 1)
]
@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):
for book in BOOKS:
if book.id == book_id:
return book
def find_book_id(book: Book):
book.id = 1 if len(BOOKS) == 0 else BOOKS[-1].id + 1
return book