Skip to main content

Books API Reference

Full endpoint reference for the live demo Books API implemented in main.py.

Base URL: http://localhost:8000/api/v1

The API uses FastAPI request validation, UUID path parameters, and an in-memory store seeded with three books at startup. This is still a demo app, but the resource contract is intentionally realistic because it is used to illustrate the API concepts discussed throughout the docs.

Endpoints

GET /books

List all books with pagination.

ParameterTypeDefaultDescription
skipinteger0Number of items to skip
limitinteger20Max items to return (1–100)
curl "http://localhost:8000/api/v1/books?skip=0&limit=10"

Response 200:

{
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "Clean Code",
"author": "Robert C. Martin",
"year": 2008,
"created_at": "2026-03-14T10:00:00Z"
}
],
"total": 3,
"skip": 0,
"limit": 10
}

POST /books

Create a new book. Returns 201 Created.

curl -X POST http://localhost:8000/api/v1/books \
-H "Content-Type: application/json" \
-d '{
"title": "The Pragmatic Programmer",
"author": "David Thomas, Andrew Hunt",
"year": 2019
}'

Request body:

{
"title": "string (required, 1–200 chars)",
"author": "string (required, 1–100 chars)",
"year": "integer (1900–2100)"
}

Response 201: Full BookResponse object with generated id and created_at values.


GET /books/{book_id}

Get a single book by UUID.

curl http://localhost:8000/api/v1/books/3fa85f64-5717-4562-b3fc-2c963f66afa6

Response 404 if not found:

{ "detail": "Book not found" }

PATCH /books/{book_id}

Partial update — only fields included in the request body are changed.

curl -X PATCH http://localhost:8000/api/v1/books/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
-H "Content-Type: application/json" \
-d '{"year": 2022}'

Uses model_dump(exclude_unset=True) internally — fields omitted from the payload are untouched.

Empty payloads are valid and return the existing book unchanged.


DELETE /books/{book_id}

Delete a book. Returns a confirmation message.

curl -X DELETE http://localhost:8000/api/v1/books/3fa85f64-5717-4562-b3fc-2c963f66afa6

Response 200:

{ "message": "Book 3fa85f64-... deleted successfully" }

GET /books/search

Search books by title or author substring (case-insensitive).

ParameterTypeRequiredDescription
qstringyesSearch query (min 1 char)
skipintegernoPagination offset
limitintegernoMax results
curl "http://localhost:8000/api/v1/books/search?q=clean"

The search is case-insensitive and matches both title and author.

Supporting endpoints

EndpointPurpose
GET /Root metadata payload with docs URL and traffic status
GET /api/psHealth payload with current book count
HEAD /api/statusHeader-only monitoring check
OPTIONS /api/optionsSupported method list
GET /api/redirect307 redirect to a random seeded book

Error responses

StatusMeaning
422 Unprocessable EntityValidation error (Pydantic) — body contains field-level errors
404 Not FoundBook UUID not in store
405 Method Not AllowedWrong HTTP verb
400 Bad RequestInvalid X-Force-Error header when chaos mode is enabled
500 / 503Forced demo failures when chaos mode is enabled

Forced demo failures are disabled by default. To enable them locally:

ENABLE_CHAOS_HEADERS=true uvicorn main:app --reload --no-access-log

Example 422:

{
"detail": [
{
"type": "string_too_short",
"loc": ["body", "title"],
"msg": "String should have at least 1 character",
"input": ""
}
]
}

Data model

class BookResponse(BaseModel):
id: UUID
title: str
author: str
year: int
created_at: datetime

Internal fields (id, created_at) are never accepted in request bodies — separate BookCreate and BookUpdate models enforce this boundary.