FastAPI - Beginner to Pro / Project 2- Move Fast With FastAPI

Fetch Book by Id

๐Ÿ” 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 book

Now 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/3

FastAPI interprets this as:

  • book_id = 3

โš™๏ธ What Happens Behind the Scenes

When a request is made to /books/3:

  1. FastAPI matches /books/{book_id}

  2. Extracts 3 from the URL

  3. Validates it as an int (since book_id: int)

  4. 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 book

Hereโ€™s whatโ€™s happening:

  • We iterate through the in-memory BOOKS list.

  • We compare each bookโ€™s id with the requested book_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 course

Right 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>
  1. Locate the GET /books/{book_id} endpoint.

  2. Click on it.

  3. Click Try it out.

  4. Enter a valid book ID.

  5. 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