Programování

FastAPI: REST API v Pythonu od nuly krok za krokem

Postavte funkční REST API ve FastAPI – endpointy, validace přes Pydantic, stavové kódy, závislosti a automatická dokumentace. S kódem, který rovnou spustíte.

12. 10. 2026 · 4 min čtení

FastAPI je webový framework pro tvorbu API v Pythonu postavený na typových anotacích. Z typů odvodí validaci vstupu, serializaci výstupu i interaktivní dokumentaci. V tomto návodu postavíme jednoduché API pro správu úkolů (todo) – od instalace po testování.

Instalace

python3 -m venv .venv
source .venv/bin/activate
pip install "fastapi[standard]"

Extra standard přidá server Uvicorn a CLI příkaz fastapi (dostupný od FastAPI 0.111).

První endpoint

Vytvořte main.py:

from fastapi import FastAPI

app = FastAPI(title="Todo API")

@app.get("/")
def root():
    return {"status": "ok"}

Spuštění ve vývojovém režimu s automatickým reloadem:

fastapi dev main.py

Otevřete http://127.0.0.1:8000/docs – uvidíte Swagger UI s vaším endpointem. Alternativní dokumentace běží na /redoc.

Modely a validace

Data popíšeme Pydantic modely. Oddělíme model pro vstup (co posílá klient) a pro výstup (co vracíme):

from pydantic import BaseModel, Field

class TodoCreate(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    done: bool = False

class Todo(TodoCreate):
    id: int

Když klient pošle prázdný title nebo místo bool text, FastAPI automaticky vrátí 422 Unprocessable Entity s popisem chyby. Nemusíte psát žádné if.

CRUD endpointy

Pro jednoduchost ukládáme do slovníku v paměti:

from fastapi import FastAPI, HTTPException, status

app = FastAPI(title="Todo API")
db: dict[int, Todo] = {}
next_id = 1

@app.get("/todos", response_model=list[Todo])
def list_todos(done: bool | None = None):
    items = list(db.values())
    if done is not None:
        items = [t for t in items if t.done == done]
    return items

@app.get("/todos/{todo_id}", response_model=Todo)
def get_todo(todo_id: int):
    if todo_id not in db:
        raise HTTPException(status_code=404, detail="Úkol nenalezen")
    return db[todo_id]

@app.post("/todos", response_model=Todo, status_code=status.HTTP_201_CREATED)
def create_todo(data: TodoCreate):
    global next_id
    todo = Todo(id=next_id, **data.model_dump())
    db[next_id] = todo
    next_id += 1
    return todo

@app.put("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: int, data: TodoCreate):
    if todo_id not in db:
        raise HTTPException(status_code=404, detail="Úkol nenalezen")
    db[todo_id] = Todo(id=todo_id, **data.model_dump())
    return db[todo_id]

@app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_todo(todo_id: int):
    if db.pop(todo_id, None) is None:
        raise HTTPException(status_code=404, detail="Úkol nenalezen")

Všimněte si, jak FastAPI rozlišuje parametry:

  • todo_id je v cestě ({todo_id}) → path parametr, převede se na int.
  • done v cestě není a je to jednoduchý typ → query parametr (/todos?done=true).
  • data: TodoCreate je Pydantic model → tělo požadavku (JSON).

Vyzkoušení přes curl

curl -X POST localhost:8000/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Napsat API"}'

curl localhost:8000/todos?done=false
curl -X DELETE localhost:8000/todos/1 -i

Závislosti (Depends)

Systém závislostí je jedna z nejsilnějších funkcí FastAPI. Hodí se pro autentizaci, připojení k databázi nebo sdílené parametry:

from typing import Annotated
from fastapi import Depends, Header

def require_api_key(x_api_key: Annotated[str, Header()]):
    if x_api_key != "tajny-klic":
        raise HTTPException(status_code=401, detail="Neplatný klíč")

@app.delete("/todos", dependencies=[Depends(require_api_key)])
def clear_todos():
    db.clear()
    return {"deleted": True}

Hlavička X-API-Key se z názvu parametru odvodí automaticky (podtržítka → pomlčky). Klíč samozřejmě v reálu načtěte z proměnné prostředí, ne z kódu.

Testování

FastAPI má TestClient postavený na httpx:

# test_main.py
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_create_and_get():
    r = client.post("/todos", json={"title": "Test"})
    assert r.status_code == 201
    todo_id = r.json()["id"]
    assert client.get(f"/todos/{todo_id}").json()["title"] == "Test"

def test_validation():
    r = client.post("/todos", json={"title": ""})
    assert r.status_code == 422
pip install pytest
pytest -q

sync, nebo async?

Funkce můžete psát jako def i async def. Obyčejné def FastAPI spouští ve vlákně z poolu, takže blokující kód (např. synchronní databázový ovladač) nezablokuje server. async def používejte jen tehdy, když uvnitř opravdu voláte await – blokující volání v async def zastaví celou smyčku událostí.

Další kroky

  • Nahraďte slovník databází (SQLite přes SQLModel nebo SQLAlchemy).
  • Přidejte CORS přes fastapi.middleware.cors.CORSMiddleware, pokud API volá frontend z jiné domény.
  • Pro produkci spusťte fastapi run main.py nebo Uvicorn za reverzní proxy.

Kód z tohoto návodu si můžete nahrát do Projektu v ColdHost AI a nechat si v režimu Programování doplnit například perzistenci do SQLite – AI upraví přímo soubory a výsledek si stáhnete jako ZIP.