Area 08 // BackendLesson 126 / 14015 min read
REST APIs with FastAPI
A REST API exposes your app's data and actions as resources at URLs that clients use with standard HTTP methods, and FastAPI is a popular Python framework for building them quickly, with request validation and interactive OpenAPI documentation generated from your code.
Think of it like this
Analogy
A well-run library: every book has a shelf address (a URL), and there are a few standard actions anyone understands: borrow (GET), donate (POST), replace a copy (PUT), fix a page (PATCH), withdraw (DELETE). The catalogue at the entrance describes every shelf and rule (the OpenAPI docs). Visitors don't need to learn special procedures for each book.
The problem it solves
Your RAG app, agent or chatbot needs a backend that web apps, mobile apps, other services and AI agents can call. Ad-hoc endpoints like /doStuff?mode=3 are hard to use, document and secure. REST conventions make APIs predictable, and FastAPI removes most of the boilerplate: parsing, validation, errors and docs.
How it works: REST APIs
- Resources are nouns:
/documents,/documents/{id},/conversations/{id}/messages. - Methods are verbs: GET list or read, POST create (or run an action like
/ask), PUT replace, PATCH partially update, DELETE remove. - JSON bodies in and out; consistent field names.
- Status codes that mean what they say (HTTP).
- Stateless requests: each carries what's needed (auth token, parameters); server-side state lives in databases (databases).
- Lists are paginated (pagination); filters go in query parameters (
?status=open). - Version the API (
/v1) and avoid breaking changes within a version. - AI endpoints often aren't pure CRUD:
POST /askorPOST /chatruns an action; long ones return a job (async jobs); streaming ones use SSE.
How it works: building an API with FastAPI
- Create the app:
app = FastAPI(title=..., version=...). - Declare data models with Pydantic (validation): input models and output models.
- Write path operations:
@app.post("/documents", status_code=201, response_model=Doc)on a normal (orasync) function. - Path and query parameters come from function arguments with type hints (
doc_id: int). - Dependencies (
Depends) inject shared things: database sessions, the current user (auth), the LLM client (swappable in tests). - Errors:
raise HTTPException(404, "not found"). - Run it:
uvicorn app:app --reloadlocally; in production behind a process manager or container (Docker).
How it works: API docs with OpenAPI
- FastAPI generates an OpenAPI document at
/openapi.jsonfrom your routes and models. - Interactive docs at
/docs(Swagger UI) and/redoclet you try requests in the browser. - Add descriptions, examples and response codes so the docs are useful.
- Generate clients (TypeScript, Python) from the spec; agents and tools can also read it to call your API.
- Keep it accurate by testing that the spec matches behaviour.
See it in code
- Install
pip install fastapi httpx- Needs
- Python 3.10+. Run it for real with: pip install uvicorn, then uvicorn file:app --reload, and open /docs.
A small CRUD API for documents, exercised with the test client, plus the OpenAPI paths it generates.
from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI(title="Documents API", version="1.0")
class DocIn(BaseModel):
title: str
text: str
class Doc(DocIn):
id: int
DB: dict[int, Doc] = {}
@app.post("/documents", status_code=201, response_model=Doc) # create
def create(doc: DocIn):
new = Doc(id=len(DB) + 1, **doc.model_dump())
DB[new.id] = new
return new
@app.get("/documents", response_model=list[Doc]) # list
def list_docs():
return list(DB.values())
@app.get("/documents/{doc_id}", response_model=Doc) # read one
def read(doc_id: int):
if doc_id not in DB:
raise HTTPException(404, "document not found")
return DB[doc_id]
@app.delete("/documents/{doc_id}", status_code=204) # delete
def delete(doc_id: int):
DB.pop(doc_id, None)
c = TestClient(app)
print(c.post("/documents", json={"title": "Leave", "text": "25 days"}).status_code, "created")
print(c.post("/documents", json={"title": "Expenses", "text": "Meals 40/day"}).json())
print(c.get("/documents").status_code, [d["title"] for d in c.get("/documents").json()])
print(c.delete("/documents/1").status_code, "deleted")
print(c.get("/documents/1").status_code, c.get("/documents/1").json())
spec = c.get("/openapi.json").json() # generated automatically
print("OpenAPI paths:", {p: sorted(ops) for p, ops in spec["paths"].items()})201 created
{'title': 'Expenses', 'text': 'Meals 40/day', 'id': 2}
200 ['Leave', 'Expenses']
204 deleted
404 {'detail': 'document not found'}
OpenAPI paths: {'/documents': ['get', 'post'], '/documents/{doc_id}': ['delete', 'get']}The in-memory dict stands in for a database. The capstone RAG app is built the same way, with GET /health, GET /ready and POST /ask.
A real example
Example
An API that agents can use too
A team exposed their knowledge base through a FastAPI service: GET /v1/articles?query=..., GET /v1/articles/{id}, POST /v1/answers. Because the OpenAPI spec had clear descriptions and examples, the frontend team generated a typed TypeScript client in minutes, and later the same spec was wrapped as tools for an internal agent (MCP) with almost no extra code. When they needed a breaking change to the answer format, they added /v2/answers and kept /v1 running for six months.
The easy way
Start here
Start with FastAPI, one Pydantic model per request and response, HTTPException for errors, and the auto-generated /docs page as your documentation and test console.
When you grow: the production setup
Production APIs add versioning, authentication dependencies, consistent error formats, pagination and filtering conventions, rate limiting, request IDs and structured logs, OpenAPI linting and contract tests, generated clients, and deployment behind a load balancer with health checks.
Trade-offs
What you gain
- Predictable, widely understood interface.
- FastAPI gives validation and docs for free.
- Easy to call from any language, tool or agent.
What it costs you
- Some AI actions don't fit CRUD neatly.
- Chatty clients may need many requests (GraphQL or batch endpoints can help).
- Versioning and backward compatibility take discipline.
When not to use it
- Real-time two-way interaction (voice, collaborative editing) suits WebSockets better than plain REST.
Common mistakes
- Verbs in URLs (
/getDocuments) and everything as POST. - Returning database models directly, leaking internal fields.
- No pagination on list endpoints.
- Breaking response formats without a new version.
- Leaving
/docspublicly open for internal-only APIs without auth.
Interview questions
Try answering out loud first, then open the model answer.
What makes an API RESTful?
Model answer
Resources identified by URLs, standard HTTP methods with their usual meanings, stateless requests, standard status codes, representations such as JSON, and use of HTTP features like caching headers. In practice: noun-based paths, consistent JSON, correct codes, pagination and versioning.
Why is FastAPI popular for AI backends?
Model answer
It's Python (where AI libraries live), async-capable for many concurrent I/O-bound LLM calls, uses type hints and Pydantic for automatic validation and serialisation, generates OpenAPI docs, has a simple dependency-injection system, and performs well.
How would you design the API for a RAG chatbot?
Model answer
Resources for conversations and messages (POST /v1/conversations, GET /v1/conversations/{id}, POST /v1/conversations/{id}/messages which streams the answer over SSE), documents (POST /v1/documents returning a job ID for ingestion, GET /v1/documents?cursor=), and health endpoints; authentication on every route, rate limits per user, consistent errors, and an OpenAPI spec.
Quiz
Pick an answer to see whether it’s right and why.
0 of 5 answered
Key terms
- REST API
- An API that exposes resources at URLs and uses standard HTTP methods and status codes to work with them.
- Endpoint
- One URL and method in an API that does a specific job, such as POST /ask.
- FastAPI
- A popular Python web framework for building APIs, using type hints and Pydantic for validation and automatic docs.
- OpenAPI
- A standard, machine-readable description of an HTTP API's endpoints, inputs and outputs, used to generate docs, clients and tests. FastAPI produces it automatically.
- CRUD
- Create, Read, Update and Delete, the four basic operations on stored data, usually mapped to POST, GET, PUT or PATCH, and DELETE.
Before and after
Progress is saved in this browser only.