✏️ Creating a PUT Endpoint to Update a Book
In this phase, we will implement a PUT endpoint that allows users to update an existing book.
This introduces an important backend concept:
Updating an existing resource using an HTTP PUT method.
📌 Define the PUT Endpoint
Here is the implementation:
@app.put("/books/update_book")
async def update_book(book: BookRequest):
for i in range(len(BOOKS)):
if BOOKS[i].id == book.id:
BOOKS[i] = book
return BOOKS[i]Now let’s break this down step by step 👇
🔍 Understanding the Function Logic
👉 The Route
@app.put("/books/update_book")This defines a PUT HTTP method
The endpoint path is
/books/update_bookIt signals that we are modifying existing data
👉 The Request Body
async def update_book(book: BookRequest):Notice:
The function expects a
BookRequestThis means FastAPI will:
Parse the JSON body
Validate it using Pydantic
Convert it into a Python object
⚠ Important:
The request body must include the book’s
id.
🧐 Why?
Because the system needs to know which book to update.
🧠 How FastAPI Processes This
When a client sends a PUT request:
JSON body is received.
FastAPI validates it against
BookRequest.If validation fails → 422 error.
If validation succeeds → function executes.
Again:
Validation happens before business logic.
🔄 How the Update Logic Works
for i in range(len(BOOKS)):We loop through the list using index positions.
🧐 Why use range(len()) instead of for book in BOOKS?
Because we need the index position to replace the object directly.
if BOOKS[i].id == book.id:We compare:
Existing book ID
Incoming request book ID
If they match:
BOOKS[i] = bookWe replace the old book with the new one.
Then:
return BOOKS[i]We return the updated resource.
⚠ Important Backend Observation
This is simulating what a database update would look like:
UPDATE books SET title = 'New Title' WHERE id = 3;
# We will discuss this in much detail later on in the courseRight now, we are modifying an in-memory list.
But conceptually:
We locate the record.
We replace it.
We return the updated resource.
This is classic backend CRUD behavior.
🌐 Testing the Endpoint in Swagger
Now let’s verify the behavior.
🖥 Open:
<http://127.0.0.1:8000/docs>Locate PUT /books/update_book
Click Try it out
Provide:
An existing book.
Updated title/author, etc.
Click Execute
✔ The book will be updated.
🧠 Improving the Endpoint (Status Codes Concept)
Right now, this endpoint:
Always returns
200 OKif it finds the book.Returns
[]with200 OKif no book matches.
This is not ideal in a real backend system.
Later, we will improve this by:
Returning
404 Not Foundif the book does not exist.Returning proper HTTP status codes.
Professional APIs do not always return 200.
They reflect the actual outcome of the request.
🗂️ 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))
def find_book_id(book: Book):
if len(BOOKS) > 0:
book.id = BOOKS[-1].id + 1
else:
book.id = 1
return book
@app.get("/books/{book_id}")
async def read_book(book_id: int):
for book in BOOKS:
if book.id == book_id:
return book
@app.get("/books/")
async def read_book_by_rating(book_rating: int):
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_book")
async def update_book(book: BookRequest):
for i in range(len(BOOKS)):
if BOOKS[i].id == book.id:
BOOKS[i] = book
def find_book_id(book: Book):
book.id = 1 if len(BOOKS) == 0 else BOOKS[-1].id + 1
return book