π Replace Body() With Pydantic Validation (POST)
Earlier, we used Body() to read request data.
But it did not provide validation or type safety.
π Using Pydantic ensures incoming data is:
β Properly formatted
β Strongly typed
β Automatically validated
π¦ Step 1 β Importing Pydantic
FastAPI includes Pydantic by default β no extra installation is required.
from fastapi import FastAPI, Body # we have removed Body
from pydantic import BaseModel # π FastAPI includes Pydantic by defaultπ Step 2 β Existing Book Class (Internal Object)
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π This is a plain Python class used inside the app.
β Represents a trusted object
β No validation
β No automatic type checking
π§± Step 3 β Create Pydantic Request Model
class BookRequest(BaseModel):
id: int
title: str
author: str
description: str
rating: intπ This defines the structure of incoming JSON data.
FastAPI will automatically:
Validate data types
Reject invalid/missing fields
Return HTTP 422 on errors
Show schema in Swagger UI
π‘ Think of it as an API boundary guard.
π Step 4 β Updating the POST Endpoint
Previously, we used Body().
Now, we replace it with a typed parameter:
β # Old version using Body()
@app.post("/create-book")
async def create_book(book_request=Body()):
BOOKS.append(book_request)
β
# New version using Pydantic
@app.post("/create_book")
async def create_book(book_request: BookRequest):
BOOKS.append(book_request)π Testing in the Browser
POST request with Pydantic in Swagger UI
π What Just Happened?
When a request is sent:
π FastAPI will:
Parse the JSON body
Convert it into a
BookRequestobjectValidate it
Reject it if invalid
β οΈ Problem: book_request is a BookRequest object, not a Book.
Mixing types in BOOKS leads to inconsistent data.
π§ͺ Verifying the Request Type
Letβs confirm what FastAPI is actually passing into our function.
@app.post("/create_book")
async def create_book(book_request: BookRequest):
print(type(book_request)) # π confirms type of book_request.
BOOKS.append(book_request)π Testing in the Browser
Printing and verifying BookRequest type
When you send a request, the terminal will display:
<class 'books2.BookRequest'> # π this means its of type BookRequest.This confirms:
β Request body was parsed
β Data was validated
β We received a Pydantic model instance
Not a dictionary.
π Step 5 β Convert to Book Object
@app.post("/create_book")
async def create_book(book_request: BookRequest):
new_book = Book(**book_request.model_dump())
BOOKS.append(new_book)π Whatβs Happening?
π model_dump()
Converts the Pydantic model into a dictionary.
book_request.model_dump()(In Pydantic v2, this replaces .dict())
π * (Dictionary Unpacking)
Book(**data)Is equivalent to:
Book(id=data["id"],
title=data["title"],
author=data["author"],
description=data["description"],
rating=data["rating"]
)π§ͺ Verifying the Conversion
Letβs confirm we are now storing a Book object.
@app.post("/create_book")
async def create_book(book_request: BookRequest):
new_book = Book(**book_request.model_dump())
print(type(new_book))
BOOKS.append(new_book)π Testing in the Browser
Verifying Book object type after conversion
When you send a request, the terminal will display:
<class 'books2.Book'>This confirms:
β Validation occurred
β Data was converted
β Only
Bookobjects are stored
π§ Why This Architecture Matters
You now have a clear separation between:
Layer | Purpose |
|---|---|
| API boundary validation |
| Internal business object |
This improves:
π Data integrity
π Scalability
π§© Maintainability
π Professional architecture
ποΈ books2.py
from fastapi import FastAPI
from pydantic import BaseModel
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: int
title: str
author: str
description: str
rating: int
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(new_book)