4.2 · Web APIs with Flask and FastAPI

Level 4 · Specializations (Web, Data, AI, Automation)

4.2Web APIs with Flask and FastAPI

Goal: Create a lightweight REST API with Flask or FastAPI (JSON resources, Pydantic validation, auto documentation) — to serve your data to any client instead of locking it inside pages.
Estimated time: 13 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • API = JSON contract: resources (GET /articles, POST /articles) and statuses that tell the truth (200 read, 201 created, 400 invalid, 404 not found).
  • Flask = minimalist freedom (small services, learning); FastAPI = modern: type hints → auto validation + interactive /docs documentation, native async.
  • Nabil's reflex: design resources and statuses BEFORE the code, validate every input with Pydantic, test with TestClient.

Running mini-case — Nabil serves his sales as JSON

Nabil's Django back office (4.1) is useful, but a colleague wants the sales figures in a separate dashboard: he needs JSON, not HTML pages. Nabil builds a small FastAPI API in Algiers: GET /ventes to read, POST /ventes (201 if created, 400 if invalid) to add, with Pydantic validation and interactive documentation on /docs. The colleague explores the API alone, without calling Nabil. Second milestone of the running thread: the same data, served to all clients.

Sequel to case 4.1 — Nabil decouples his data from its display.

1. REST APIs: resources, JSON and statuses

A REST API exposes data as JSON via URLs and HTTP methods: GET /ventes reads, POST /ventes creates, and the HTTP status tells the result — 200 read, 201 created, 400 invalid input, 404 not found. JSON (keys/values, lists) maps exactly to Python dicts and lists: it is the universal format. Nabil designs his resources and statuses first — the contract — before writing a single line.

2. Minimalist Flask, modern FastAPI

Flask is a micro-framework: one route = one URL to one function, total freedom, ideal for learning and small services. FastAPI is its modern successor: type hints (titre: str) feed Pydantic which validates each input automatically, the OpenAPI documentation generates itself (interactive /docs page), and native async (async/await) handles simultaneous requests without blocking. Nabil chooses FastAPI for his sales API: validation + living documentation with no effort.

And we make it reliable: test each route with pytest + TestClient (valid POST → 201, invalid POST → 400), and document via /docs rather than a Word document that goes stale. A tested, self-documented API is a contract colleagues use without bothering you.

Common misinterpretation: “Returning 200 for everything, the response body is enough.” No. The HTTP status is the first information the client reads: a 200 on a creation prevents distinguishing read/created, a 200 on an error breaks all automatic error handling. Truthful statuses (201, 400, 404) are the contract; JSON is only the content.
The flow of an API request Route → validation → logic → JSON + status Route URL + method tothe functionValidation (Pydantic) Conforminginput? otherwise 400Logic Read or storethe resourceJSON response + status 200 read,201 created, 404 missing
Route → validation → logic → JSON + status
Text version of the diagram (SEO / screen readers / translation): 1) Nabil declares his routes: URL + HTTP method to a function. 2) Pydantic validates each input from type hints (400 if invalid). 3) The function reads or stores the resource. 4) The API answers in JSON with the right status. Lesson: contract first, auto validation, living documentation.

🔎 Concrete example — 🔎 Concrete example — Nabil's sales API

Nabil's moves with the real FastAPI APIs:

class Vente(BaseModel): ville: str; montant: float  # auto-validated
@app.get("/ventes") → list as JSON (200)
@app.post("/ventes", status_code=201) → creates (201) or 400 if invalid
uvicorn main:app --reload  +  doc on /docs

Reading: validated model, two routes, explicit statuses.

Lesson: hints give validation + documentation.

Section vocabulary

REST API
Data exposed as JSON via URLs and HTTP methods (GET read, POST create), with statuses telling the result.

Simple example: Nabil's API: GET /ventes reads, POST /ventes creates (201).

What it is not: HTML pages reserved for a single display.

Reflex: design resources + statuses before the code.

Flask
Minimalist Python micro-framework: one route = one URL to one function; total freedom, ideal for learning and small services.

Simple example: Nabil sketches his first API prototype there in a few lines.

What it is not: a complete framework imposing admin and ORM.

Reflex: small tailor-made service? Flask is enough.

FastAPI + Pydantic
Modern API framework: type hints feed Pydantic which validates inputs, generates OpenAPI documentation and supports async.

Simple example: Nabil's Vente is validated (amount number required) with no control code.

What it is not: inputs accepted with no verification.

Reflex: type each input model, test the 400.

HTTP statuses
Result codes: 200 read, 201 created, 400 invalid input, 404 not found — the contract read first by the client.

Simple example: Nabil's POST answers 201 if created, 400 if the amount is missing.

What it is not: a 200 returned for everything, successes and errors.

Reflex: each route: which status for which case?

In practice — In practice — Serve a FastAPI API

  1. Install fastapi and uvicorn in a venv, then declare a Pydantic model (e.g. Vente: ville str, montant float).
  2. Add GET /ventes (read, 200) and POST /ventes (create, status_code=201) on an in-memory list.
  3. Launch with “uvicorn main:app --reload” and explore the interactive documentation on /docs: test from the browser.
  4. Write 2 tests (pytest + TestClient): valid POST → 201, invalid POST → 400; fix until green.
You serve a validated, documented, tested REST API: your data becomes consumable by any client.

Ready-to-say script — presenting your API (30 s)

“This is Nabil: our sales are served as JSON — GET to read, POST in 201 to create, 400 if the input is invalid. Pydantic validates everything, the documentation on /docs is up to date by default, and tests lock the contract.”

Say in a demo; show /docs live.

Copy-paste template — FastAPI API (tested code)

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Vente(BaseModel):
    ville: str
    montant: float

VENTES: list[Vente] = []

@app.get("/ventes")
def lister():
    return VENTES

@app.post("/ventes", status_code=201)
def creer(vente: Vente):
    VENTES.append(vente)
    return vente

# Run: uvicorn main:app --reload
# Interactive doc: /docs
# Test:
# from fastapi.testclient import TestClient
# client = TestClient(app)
# assert client.post("/ventes", json={"ville": "Alger", "montant": 1200.5}).status_code == 201
# assert client.post("/ventes", json={"ville": "Alger"}).status_code == 422

Quick prototype

Flask in a few lines: one route, JSON, and your idea is demonstrable tonight.

Team API

Like Nabil: FastAPI + Pydantic everywhere, /docs as official documentation, TestClient as routine.

Heavy traffic

Turn demanding views into async (async def): network waits no longer pile up requests.

Django data

Keep your Django models (4.1) and expose them as JSON: same data, new contract, zero rewriting.

Caution: An open API is a door: validate every input with Pydantic and return only what is needed, never superfluous personal data. Educational content, examples with fictitious data.

✓ Best practices

  • Design the contract (resources + statuses) before the code.
  • Validate every input with Pydantic.
  • Test with TestClient and document via /docs.

✗ Common pitfalls

  • Returning 200 for everything, successes and errors.
  • Accepting unvalidated inputs.
  • Maintaining a separate documentation that goes stale.

Key takeaways

  • REST API = JSON via URL + HTTP METHODS; statuses that tell the truth (200/201/400/404).
  • Minimalist FLASK (freedom, small services); modern FASTAPI (auto validation, /docs, async).
  • PYDANTIC validates from type hints; TESTCLIENT locks the contract.
  • Same data, several clients: decouple exposure from display.

48h challenge — apply tomorrow

Expose your own data: GET + POST with Pydantic model, correct 201/400, explored /docs and 2 green tests.

Section exit checklist
  • I can name resources, JSON, statuses 200/201/400/404.
  • I can explain Pydantic validation and /docs.
  • I have 1 tested API serving my data.
Self-rating 1-5: does each route return the right status?are all my inputs validated?

Frequently asked questions

Why is JSON everywhere in APIs?

Because it is a lightweight text format of keys/values and lists matching Python dicts and lists exactly: human-readable, manipulable in one line of code, understood by all languages. The client receives JSON, converts it to native objects, and works — with no driver or proprietary format.

What is async (async/await) for in FastAPI?

To handle many simultaneous requests without blocking: while one request waits for the database or network, the server handles the others instead of waiting. For an API reading data or calling services, async clearly increases throughput with the same server — without changing the logic.

Check your understanding

What does a REST API expose to its clients?

Additional resources (optional, to go further)

Level 4 examFresh, harder questions to test what you've learned