Pytania rekrutacyjne FastAPI

70 pytań — 20 odpowiedzi dostępnych od razu

Darmowe pytania rekrutacyjne FastAPI pomagają przygotować się do rozmowy o budowaniu nowoczesnego API w Pythonie. Ten podgląd zawiera przykładowe pytania i odpowiedzi z pełnej puli 70 zagadnień.

Fiszki obejmują routing i parametry, modele Pydantic, walidację, serializację oraz wstrzykiwanie zależności. Materiał pomaga przećwiczyć projektowanie endpointów i wyjaśnianie kluczowych mechanizmów FastAPI.

Darmowy start

Pierwsze 20 odpowiedzi są dostępne od razu

Przeglądaj pełną listę 70 pytań. Pełne odpowiedzi, przykłady kodu i materiały do nauki odblokujesz w panelu.

Odblokuj 50 odpowiedzi

Podstawy FastAPI

Czym jest FastAPI i jakie problemy rozwiązuje?

Odpowiedź w 30 sekund: FastAPI to nowoczesny, wysokowydajny framework webowy do budowania API w Pythonie, oparty na standardowych adnotacjach typów. Rozwiązuje problem ręcznej walidacji danych, serializacji i braku dokumentacji — automatycznie waliduje żądania, serializuje odpowiedzi i generuje interaktywną dokumentację OpenAPI. Dzięki wsparciu dla async/await osiąga wydajność porównywalną z Node.js i Go.

Odpowiedź w 2 minuty: FastAPI powstał, aby wyeliminować powtarzalny i podatny na błędy kod, który wcześniej trzeba było pisać ręcznie w starszych frameworkach. W klasycznym Flasku programista sam parsuje ciało żądania, sprawdza typy, zwraca komunikaty błędów i osobno opisuje API w dokumentacji. FastAPI robi to wszystko automatycznie na podstawie deklaracji typów w sygnaturze funkcji obsługującej endpoint.

Główne problemy, które rozwiązuje, to: automatyczna walidacja danych wejściowych (przez Pydantic) wraz z czytelnymi, ustrukturyzowanymi błędami; automatyczna serializacja danych wyjściowych zgodnie z modelem odpowiedzi; samogenerująca się dokumentacja w standardzie OpenAPI (dostępna pod /docs jako Swagger UI oraz /redoc jako ReDoc); oraz natywne wsparcie dla programowania asynchronicznego, co pozwala obsługiwać wiele równoczesnych połączeń bez blokowania wątku.

Kolejną wartością jest doskonałe wsparcie edytora. Ponieważ wszystko opiera się na adnotacjach typów, IDE podpowiada pola, wykrywa błędy przed uruchomieniem i ułatwia refaktoryzację. FastAPI jest też świadomie zaprojektowany wokół standardów (OpenAPI, JSON Schema, OAuth2), więc integruje się z istniejącym ekosystemem narzędzi.

Typowa pułapka początkujących: traktowanie FastAPI jak frameworka pełnostackowego w stylu Django. FastAPI to framework do API — nie ma wbudowanego ORM, panelu administracyjnego ani systemu szablonów (choć można je dodać). Drugie błędne przekonanie to to, że "FastAPI jest szybki, więc wszystko piszę jako async" — jeśli wewnątrz endpointu wywołasz blokującą operację synchroniczną (np. zwykły sterownik bazy danych), zablokujesz całą pętlę zdarzeń.

Przykład kodu:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# Model wejściowy — FastAPI automatycznie zwaliduje dane
class Produkt(BaseModel):
    nazwa: str
    cena: float

@app.post("/produkty/")
async def utworz_produkt(produkt: Produkt):
    # Dane są już zwalidowane i poprawnie otypowane
    return {"komunikat": f"Dodano {produkt.nazwa} za {produkt.cena} zł"}

Materiały

Na czym opiera się FastAPI (Starlette i Pydantic) i jaką rolę pełni każdy z tych komponentów?

Odpowiedź w 30 sekund: FastAPI stoi na dwóch filarach: Starlette odpowiada za warstwę webową (routing, obsługa żądań/odpowiedzi, ASGI, asynchroniczność), a Pydantic za walidację i serializację danych w oparciu o adnotacje typów. FastAPI sam jest cienką, ale potężną warstwą spajającą oba narzędzia i dodającą automatyczną dokumentację OpenAPI.

Odpowiedź w 2 minuty: Starlette to lekki toolkit/framework ASGI, który dostarcza całą "mechanikę" serwera webowego: routing URL-i, obiekty Request i Response, middleware, obsługę WebSocketów, zadania w tle (background tasks), CORS oraz sesje. FastAPI dziedziczy po Starlette, więc wszystko, co potrafi Starlette, działa również w FastAPI. To Starlette zapewnia natywne wsparcie async/await i wysoką wydajność warstwy sieciowej.

Pydantic odpowiada za warstwę danych. Definiujesz modele jako klasy dziedziczące po BaseModel, a Pydantic na ich podstawie waliduje dane wejściowe, konwertuje typy, generuje czytelne błędy i serializuje dane wyjściowe. Pydantic v2 ma rdzeń napisany w Ruście (pydantic-core), dzięki czemu walidacja jest bardzo szybka. To również Pydantic generuje JSON Schema, które FastAPI wykorzystuje do budowy dokumentacji OpenAPI.

Rola samego FastAPI to inteligentne połączenie tych komponentów oraz dodanie funkcji, których żaden z nich nie ma osobno: system wstrzykiwania zależności (Dependency Injection), automatyczne mapowanie parametrów ścieżki/zapytania/ciała żądania na argumenty funkcji, wsparcie bezpieczeństwa (OAuth2, klucze API) oraz generowanie interaktywnej dokumentacji /docs i /redoc.

Warto zapamiętać podział odpowiedzialności: jeśli pytanie dotyczy "jak dane przepływają przez sieć" — to domena Starlette; jeśli "jak dane są walidowane i kształtowane" — to domena Pydantic. Częsta pułapka: mieszanie modeli Pydantic z modelami ORM (np. SQLAlchemy). To dwie różne rzeczy — model Pydantic opisuje kształt danych API, a model ORM opisuje tabelę w bazie. Zwykle definiuje się oba osobno i mapuje jeden na drugi (np. przez model_config = ConfigDict(from_attributes=True)).

Diagram:

flowchart TD
    Klient["Klient (HTTP / JSON)"] --> Serwer["Serwer ASGI (Uvicorn)"]
    Serwer --> Starlette["Starlette — warstwa webowa: routing, żądanie/odpowiedź, async"]
    Starlette --> FastAPI["FastAPI — spaja warstwy, dodaje DI i dokumentację"]
    FastAPI --> Pydantic["Pydantic — walidacja i serializacja danych"]
    Pydantic --> Funkcja["Twoja funkcja endpointu"]
    Funkcja --> Pydantic
    Pydantic --> OpenAPI["Dokumentacja OpenAPI (/docs, /redoc)"]

Materiały

Czym jest ASGI i czym różni się od WSGI? Dlaczego ma to znaczenie dla FastAPI?

Odpowiedź w 30 sekund: WSGI (Web Server Gateway Interface) to starszy, synchroniczny standard komunikacji między serwerem a aplikacją Pythona — obsługuje jedno żądanie na raz, blokując wątek. ASGI (Asynchronous Server Gateway Interface) to jego następca wspierający async/await, WebSockety i zdarzenia długo żyjące. FastAPI jest frameworkiem ASGI, dzięki czemu może obsługiwać tysiące współbieżnych połączeń bez blokowania.

Odpowiedź w 2 minuty: WSGI to standard, na którym opierają się Flask i Django (w trybie synchronicznym). Definiuje prosty kontrakt: serwer wywołuje funkcję aplikacji raz na żądanie i czeka na zakończenie. Model ten jest synchroniczny i blokujący — w trakcie operacji oczekujących (np. zapytanie do bazy, wywołanie zewnętrznego API) wątek jest zajęty i nie może obsłużyć innego żądania. Skalowanie odbywa się przez uruchamianie wielu procesów/wątków.

ASGI rozwiązuje to ograniczenie. Aplikacja ASGI to funkcja asynchroniczna przyjmująca trzy argumenty: scope (metadane połączenia), receive (do odbierania zdarzeń) i send (do wysyłania zdarzeń). Model oparty na zdarzeniach pozwala obsłużyć nie tylko HTTP, ale też WebSockety i zdarzenia cyklu życia (lifespan). Co najważniejsze — gdy endpoint async czeka na operację I/O, pętla zdarzeń może w tym czasie obsłużyć inne żądania na tym samym wątku. To daje wysoką współbieżność dla obciążeń zdominowanych przez I/O.

Dla FastAPI ma to znaczenie fundamentalne: FastAPI to framework ASGI (przez Starlette), więc natywnie wspiera async def w endpointach oraz WebSockety. Uruchamiasz go serwerem ASGI, takim jak Uvicorn (oparty na uvloop i httptools) czy Hypercorn — nie zadziała pod klasycznym serwerem WSGI typu Gunicorn z domyślnym workerem.

Najważniejsza pułapka: ASGI daje przewagę tylko wtedy, gdy faktycznie nie blokujesz pętli zdarzeń. Jeśli w endpoincie async def wywołasz operację synchroniczną i blokującą (np. time.sleep, synchroniczny sterownik bazy, ciężkie obliczenia CPU), zablokujesz cały worker i unicestwisz korzyść z asynchroniczności. Dla takich operacji albo używaj zwykłego def (FastAPI uruchomi go w puli wątków), albo wynoś ciężkie zadania poza pętlę. Drugie nieporozumienie: ASGI nie czyni kodu CPU-bound szybszym — to rozwiązanie dla obciążeń I/O-bound.

Diagram:

flowchart LR
    subgraph WSGI["WSGI — synchroniczny"]
        W1["Żądanie A"] --> W2["Wątek zajęty (czeka na I/O)"]
        W2 --> W3["Żądanie B czeka w kolejce"]
    end
    subgraph ASGI["ASGI — asynchroniczny"]
        A1["Żądanie A czeka na I/O"] --> A2["Pętla zdarzeń obsługuje żądanie B"]
        A2 --> A3["Powrót do A, gdy I/O gotowe"]
    end

Materiały

Czym FastAPI różni się od Flaska i Django (DRF) oraz kiedy wybrałbyś FastAPI?

Odpowiedź w 30 sekund: FastAPI to natywnie asynchroniczny framework do API z automatyczną walidacją (Pydantic) i dokumentacją OpenAPI, podczas gdy Flask jest minimalistycznym, synchronicznym mikroframeworkiem, a Django (z DRF) to obszerny framework pełnostackowy z ORM, panelem admina i wieloma "bateriami w zestawie". FastAPI wybierasz, gdy budujesz nowoczesne, wydajne API JSON, zależy Ci na walidacji typów, asynchroniczności i automatycznej dokumentacji.

Odpowiedź w 2 minuty: Flask jest minimalistyczny i bardzo elastyczny, ale wymaga ręcznego dokładania walidacji (np. Marshmallow), dokumentacji i obsługi async. Świetnie sprawdza się w małych usługach i prototypach oraz tam, gdzie zespół ceni pełną kontrolę i prostotę. Flask 2+ wspiera async, ale jego rdzeń pozostaje WSGI i model synchroniczny.

Django to framework pełnostackowy z mocnym ORM, migracjami, panelem administracyjnym, systemem uwierzytelniania i ekosystemem. Django REST Framework (DRF) dokłada warstwę API: serializery, widoki, uprawnienia. To doskonały wybór dla dużych aplikacji opartych na bazie danych, z bogatą logiką domenową i panelem admina. Wadą bywa "ciężkość" i historycznie synchroniczny model (choć Django ma już wsparcie ASGI).

FastAPI lokuje się pomiędzy: lekki jak Flask, ale z automatyczną walidacją, serializacją i dokumentacją "od ręki", natywnie asynchroniczny i bardzo wydajny. Nie ma wbudowanego ORM ani panelu admina — to świadomy wybór skupienia na API.

Poniższa tabela podsumowuje różnice:

Cecha FastAPI Flask Django + DRF
Typ Framework do API Mikroframework Framework pełnostackowy
Standard ASGI (async natywnie) WSGI (async ograniczony) WSGI + ASGI
Walidacja danych Wbudowana (Pydantic) Ręczna / Marshmallow Serializery DRF
Dokumentacja API Automatyczna (OpenAPI) Ręczna / dodatki Ręczna / dodatki (drf-spectacular)
ORM Brak (dowolny, np. SQLAlchemy) Brak (dowolny) Wbudowany Django ORM
Panel admina Brak Brak Wbudowany
Adnotacje typów Centralne dla działania Opcjonalne Opcjonalne
Najlepsze do Wydajne API JSON, mikroserwisy Małe usługi, prototypy Duże aplikacje z bazą i adminem

Kiedy wybrać FastAPI: gdy budujesz API JSON (zwłaszcza dla SPA/mobile), zależy Ci na wysokiej współbieżności I/O (wiele zewnętrznych wywołań, WebSockety), chcesz walidację i dokumentację bez dodatkowego kodu, oraz pracujesz z nowoczesnym Pythonem (3.9+) i adnotacjami typów. Typowa pułapka: wybór FastAPI do projektu, który tak naprawdę potrzebuje panelu admina i dojrzałego ORM-a "z pudełka" — wtedy Django bywa szybszy w dostarczeniu. Druga: oczekiwanie, że samo async przyspieszy obciążenia CPU-bound — tu wygrywa odpowiednia architektura, nie sam framework.

Materiały

Jak wygląda minimalna aplikacja FastAPI i jak ją uruchomić za pomocą uvicorn?

Odpowiedź w 30 sekund: Minimalna aplikacja to instancja FastAPI() oraz jeden endpoint udekorowany @app.get("/"). Uruchamiasz ją serwerem ASGI Uvicorn poleceniem uvicorn main:app --reload, gdzie main to nazwa pliku, a app to nazwa zmiennej z instancją aplikacji. Dokumentacja jest od razu dostępna pod /docs.

Odpowiedź w 2 minuty: Najprostsza aplikacja FastAPI mieści się w kilku liniach: importujesz klasę FastAPI, tworzysz instancję app = FastAPI(), a następnie definiujesz funkcję obsługującą żądanie i dekorujesz ją metodą HTTP (@app.get, @app.post itd.) wraz ze ścieżką. Funkcja może być zwykła (def) lub asynchroniczna (async def) — FastAPI obsłuży oba przypadki poprawnie.

Do uruchomienia potrzebujesz serwera ASGI. Najpopularniejszy jest Uvicorn. Instalujesz go (pip install "uvicorn[standard]", co dociąga uvloop i httptools dla wyższej wydajności) i uruchamiasz z linii poleceń: uvicorn main:app --reload. Flaga --reload automatycznie przeładowuje serwer po zmianie kodu — używaj jej tylko w developmencie, nigdy na produkcji. Domyślnie serwer słucha na 127.0.0.1:8000; możesz to zmienić flagami --host i --port.

Po starcie masz natychmiast dostępne: sam endpoint (np. http://127.0.0.1:8000/), interaktywną dokumentację Swagger UI pod /docs oraz alternatywną dokumentację ReDoc pod /redoc. To jedna z najmocniejszych stron FastAPI — dokumentacja powstaje sama z deklaracji endpointów i typów.

Warto znać alternatywne sposoby uruchamiania. Nowsze wersje udostępniają polecenie fastapi dev main.py (z pakietu fastapi[standard]), które jest wygodnym opakowaniem na Uvicorn z auto-reload. Możesz też uruchomić serwer programowo, wywołując uvicorn.run(...) w bloku if __name__ == "__main__":. Na produkcji często stosuje się Uvicorn zarządzany przez kilka procesów (np. Gunicorn z workerem uvicorn.workers.UvicornWorker) lub po prostu uvicorn z flagą --workers.

Typowe pułapki: błędny zapis main:app (mylenie nazwy pliku z nazwą zmiennej), uruchamianie z innego katalogu niż ten, w którym jest plik (Python nie znajdzie modułu), oraz pozostawienie --reload na produkcji. Pamiętaj też, że python main.py samo z siebie nie wystartuje serwera, jeśli nie dodasz wywołania uvicorn.run.

Przykład kodu:

# Plik: main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    # Zwrócony słownik zostanie automatycznie zserializowany do JSON
    return {"komunikat": "Witaj w FastAPI"}

@app.get("/elementy/{element_id}")
async def odczytaj_element(element_id: int, q: str | None = None):
    # element_id jest walidowany jako int, q jest opcjonalnym parametrem zapytania
    return {"element_id": element_id, "q": q}


# Opcjonalnie: uruchomienie programowe poleceniem `python main.py`
if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)

Uruchomienie z terminala:

# Zalecany sposób w developmencie
uvicorn main:app --reload

# Lub nowsze polecenie CLI z pakietu fastapi[standard]
fastapi dev main.py

Materiały

Skąd FastAPI czerpie swoją wydajność i dlaczego uchodzi za jeden z najszybszych frameworków w Pythonie?

Odpowiedź w 30 sekund: Wydajność FastAPI wynika z trzech źródeł: natywnej asynchroniczności opartej na ASGI (przez Starlette), szybkiego serwera Uvicorn (uvloop + httptools, z rdzeniem w C) oraz walidacji w Pydantic v2 napisanej w Ruście. Razem dają one przepustowość porównywalną z NodeJS i Go, co potwierdzają niezależne benchmarki (np. TechEmpower).

Odpowiedź w 2 minuty: Pierwsze źródło wydajności to model asynchroniczny. FastAPI jest frameworkiem ASGI, więc przy obciążeniach I/O-bound (zapytania do bazy, wywołania zewnętrznych API) potrafi obsługiwać wiele współbieżnych żądań na jednym wątku, nie blokując pętli zdarzeń podczas oczekiwania. To kluczowa przewaga nad synchronicznym WSGI, gdzie wątek jest zajęty przez cały czas trwania operacji.

Drugie źródło to warstwa serwera i transportu. Uvicorn, domyślny serwer dla FastAPI, używa uvloop (bardzo szybkiej implementacji pętli zdarzeń opartej na libuv, tej samej co Node.js) oraz httptools (szybkiego parsera HTTP w C). Sama warstwa webowa pochodzi ze Starlette, który jest celowo lekki i zoptymalizowany.

Trzecie źródło to Pydantic v2. Walidacja i serializacja danych — najczęściej wykonywana operacja w typowym API — została w wersji 2 przepisana z rdzeniem w Ruście (pydantic-core). To przyniosło wielokrotne przyspieszenie względem Pydantic v1, a ponieważ niemal każde żądanie przechodzi przez walidację, efekt jest odczuwalny w całej aplikacji.

Ważne zastrzeżenia i błędne przekonania. Po pierwsze — "szybki framework" nie czyni automatycznie szybką Twojej aplikacji. Jeśli zablokujesz pętlę zdarzeń synchronicznym wywołaniem w endpoincie async def, stracisz całą przewagę. Po drugie — asynchroniczność pomaga przy I/O, ale nie przy obliczeniach CPU-bound; ciężkie obliczenia i tak ograniczy GIL, więc wynoś je do osobnych procesów lub workerów. Po trzecie — benchmarki frameworków mierzą "puste" endpointy; w realnej aplikacji wąskim gardłem najczęściej jest baza danych lub zewnętrzne usługi, a nie sam framework. Po czwarte — aby skalować na produkcji, uruchamia się wiele procesów-workerów (każdy z własną pętlą zdarzeń), bo pojedynczy proces Pythona i tak korzysta z jednego rdzenia z powodu GIL.

Diagram:

flowchart TD
    Wydajnosc["Wydajność FastAPI"] --> Async["Async / ASGI — współbieżność dla I/O"]
    Wydajnosc --> Serwer["Uvicorn: uvloop + httptools (rdzeń w C)"]
    Wydajnosc --> Pydantic["Pydantic v2 — walidacja w rdzeniu Rust"]
    Async --> Efekt["Wysoka przepustowość I/O"]
    Serwer --> Efekt
    Pydantic --> Efekt

Materiały

Jaką rolę pełnią adnotacje typów (type hints) w FastAPI i co dzięki nim zyskujemy?

Odpowiedź w 30 sekund: Adnotacje typów to fundament FastAPI — to na ich podstawie framework wie, jak walidować dane wejściowe, serializować wyjściowe i generować dokumentację OpenAPI. Dzięki nim zyskujemy automatyczną walidację, konwersję typów, podpowiedzi w edytorze i samogenerującą się dokumentację, praktycznie bez dodatkowego kodu.

Odpowiedź w 2 minuty: W większości frameworków adnotacje typów są jedynie opcjonalną pomocą dla programisty i narzędzi statycznych. W FastAPI pełnią rolę aktywną — framework odczytuje je w czasie działania i na ich podstawie buduje całą logikę obsługi żądania. Gdy zadeklarujesz parametr jako int, FastAPI sprawdzi i przekonwertuje wartość; gdy jako model Pydantic — zwaliduje całe ciało żądania.

To, co zyskujesz, jest wielowarstwowe. Po pierwsze: walidacja i konwersja danych. Parametr ścieżki element_id: int zostanie automatycznie przekonwertowany z tekstu na liczbę, a jeśli się nie da — klient dostanie czytelny błąd 422. Po drugie: serializacja odpowiedzi przez response_model, co pozwala kontrolować, jakie pola opuszczają aplikację (np. ukryć hasło). Po trzecie: automatyczna dokumentacja OpenAPI/JSON Schema, która wprost odzwierciedla zadeklarowane typy. Po czwarte: wsparcie edytora — autouzupełnianie i wykrywanie błędów jeszcze przed uruchomieniem.

Współczesna, zalecana składnia używa Annotated (z typing), aby połączyć typ z metadanymi walidacji i deklaracją źródła parametru (Query, Path, Body, Depends). Annotated jest preferowany nad starszym stylem domyślnych wartości (q: str = Query(...)), ponieważ jest czytelniejszy, działa lepiej z narzędziami statycznymi i pozwala wielokrotnie używać tych samych deklaracji. Wymaga Pythona 3.9+ (a dla str | None zamiast Optional[str] — 3.10+, choć z from __future__ import annotations działa wcześniej).

Typowe pułapki i nieporozumienia. Częsty błąd to mylenie typu opcjonalnego z parametrem opcjonalnym — q: str | None mówi tylko, że wartość może być None; o tym, że parametr jest opcjonalny i jaką ma wartość domyślną, decyduje przypisanie domyślnej wartości (np. = None). Drugie nieporozumienie: oczekiwanie, że adnotacje "wymuszą" typy w całym Pythonie — poza FastAPI/Pydantic są ignorowane w czasie działania. Trzecia pułapka: pomijanie response_model, co skutkuje wyciekiem wewnętrznych pól modelu do odpowiedzi API.

Przykład kodu:

from typing import Annotated
from fastapi import FastAPI, Query, Path
from pydantic import BaseModel

app = FastAPI()

class Uzytkownik(BaseModel):
    nazwa: str
    haslo: str

class UzytkownikPubliczny(BaseModel):
    nazwa: str  # bez hasła — kontrolujemy, co wychodzi na zewnątrz

@app.get("/elementy/{element_id}")
async def odczytaj(
    # Path: walidacja parametru ścieżki, musi być > 0
    element_id: Annotated[int, Path(gt=0)],
    # Query: opcjonalny parametr zapytania z ograniczeniem długości
    q: Annotated[str | None, Query(max_length=50)] = None,
):
    return {"element_id": element_id, "q": q}

@app.post("/uzytkownicy/", response_model=UzytkownikPubliczny)
async def utworz(uzytkownik: Uzytkownik):
    # Mimo że przyjmujemy hasło, response_model usunie je z odpowiedzi
    return uzytkownik

Materiały

Routing i parametry

Jak definiuje się ścieżki (path operations) i metody HTTP w FastAPI?

Odpowiedź w 30 sekund: Ścieżki (path operations) definiuje się przez dekoratory na obiekcie aplikacji lub routera, np. @app.get("/items"), @app.post("/items"). Nazwa dekoratora odpowiada metodzie HTTP (GET, POST, PUT, DELETE, PATCH itd.), a jego argument to wzorzec ścieżki. Funkcja udekorowana staje się handlerem obsługującym żądania pod tym adresem.

Odpowiedź w 2 minuty: W FastAPI „path operation" to połączenie ścieżki URL (path) z operacją HTTP (metodą). Tworzy się je deklaratywnie: dekorujesz zwykłą funkcję Pythona dekoratorem odpowiadającym metodzie. FastAPI udostępnia @app.get, @app.post, @app.put, @app.delete, @app.patch, @app.options, @app.head oraz @app.trace. Pierwszym argumentem dekoratora jest wzorzec ścieżki, a funkcja zwraca dane, które FastAPI automatycznie serializuje do JSON (domyślnie przez JSONResponse).

Funkcje mogą być zarówno synchroniczne (def), jak i asynchroniczne (async def). Asynchroniczne handlery warto stosować, gdy w środku wykonujesz operacje I/O z bibliotekami obsługującymi await (np. asynchroniczny klient bazy danych czy HTTP). Jeśli używasz blokujących bibliotek synchronicznych, zwykły def jest bezpieczniejszy, bo FastAPI uruchomi go w puli wątków i nie zablokuje pętli zdarzeń.

Konwencje semantyczne metod HTTP są ważne w API REST: GET służy do odczytu (bez efektów ubocznych), POST do tworzenia zasobów, PUT do pełnej aktualizacji (idempotentnie), PATCH do częściowej aktualizacji, a DELETE do usuwania. FastAPI nie wymusza tych konwencji, ale trzymanie się ich poprawia czytelność API i współpracę z narzędziami. Domyślnie GET i POST zwracają status 200 OK — własny kod statusu ustawisz parametrem status_code, np. @app.post("/items", status_code=201).

Typowa pułapka: kolejność definiowania ścieżek ma znaczenie (patrz osobne pytanie), a także to, że jedna funkcja obsługuje dokładnie jedną parę (metoda + ścieżka). Aby obsłużyć tę samą ścieżkę dla różnych metod, definiujesz osobne funkcje z różnymi dekoratorami.

Przykład kodu:

from fastapi import FastAPI

app = FastAPI()


# Operacja GET — odczyt listy elementów
@app.get("/items")
async def list_items():
    return [{"id": 1, "name": "Klawiatura"}]


# Operacja POST — tworzenie nowego elementu, własny kod statusu 201
@app.post("/items", status_code=201)
async def create_item(name: str):
    return {"id": 2, "name": name}


# Operacja DELETE — usuwanie elementu o danym identyfikatorze
@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
    # Brak treści w odpowiedzi (204 No Content)
    return None

Materiały

Jaka jest różnica między parametrami ścieżki (path), zapytania (query) a ciałem żądania (body)?

Odpowiedź w 30 sekund: Parametry ścieżki są częścią adresu URL (np. /users/{id}) i zwykle identyfikują konkretny zasób — są wymagane. Parametry zapytania pojawiają się po znaku ? (np. ?q=fastapi&limit=10) i służą do filtrowania, sortowania lub paginacji — często są opcjonalne. Ciało żądania (body) to dane wysyłane w treści żądania (zwykle JSON), używane głównie w POST/PUT/PATCH do przesyłania złożonych struktur danych.

Odpowiedź w 2 minuty: FastAPI automatycznie rozpoznaje, skąd pochodzi dany parametr, na podstawie jego deklaracji w sygnaturze funkcji. Jeśli nazwa parametru występuje we wzorcu ścieżki (w nawiasach klamrowych), traktuje go jako parametr ścieżki. Jeśli parametr jest typem prostym (int, str, float, bool) i nie ma go w ścieżce, FastAPI uznaje go za parametr zapytania. Jeśli zaś typ parametru to model Pydantic (lub jest oznaczony przez Body), pobierze go z ciała żądania.

Parametry ścieżki są zawsze wymagane (są integralną częścią URL) i służą do identyfikacji konkretnego zasobu — dlatego dobrze pasuje do nich semantyka „który zasób". Parametry zapytania świetnie nadają się do opcjonalnej konfiguracji odpowiedzi: filtrowania (?status=active), paginacji (?skip=0&limit=20), sortowania czy wyszukiwania pełnotekstowego. Mogą być wymagane lub opcjonalne, a typy proste są tu naturalne. Ciało żądania używane jest, gdy klient musi przesłać złożony obiekt — np. dane nowego użytkownika z wieloma polami — i nadaje się do tego JSON walidowany przez modele Pydantic.

Częsta pułapka: GET zwykle nie powinien mieć ciała żądania (część serwerów i proxy je ignoruje), więc do GET używaj parametrów zapytania, a ciało rezerwuj dla POST/PUT/PATCH. Druga pułapka dotyczy typów: parametry zapytania i ścieżki przychodzą jako tekst z URL, więc FastAPI konwertuje je na zadeklarowany typ (np. int), a ciało walidowane jest jako JSON.

Diagram:

flowchart LR
    subgraph URL["Adres żądania POST"]
        P["/users/{id}<br/>— parametr ścieżki"]
        Q["?aktywny=true&limit=10<br/>— parametry zapytania"]
    end
    B["Treść żądania (JSON)<br/>— ciało (body)"]
    URL --> S["Serwer FastAPI"]
    B --> S
Cecha Parametr ścieżki (path) Parametr zapytania (query) Ciało żądania (body)
Gdzie w żądaniu W adresie URL: /users/42 Po znaku ?: ?q=tekst&limit=10 W treści żądania (zwykle JSON)
Typowe zastosowanie Identyfikacja zasobu Filtrowanie, sortowanie, paginacja Przesyłanie złożonych danych
Domyślnie wymagany Tak (zawsze) Może być opcjonalny Zwykle przy POST/PUT/PATCH
Typowe typy Proste (int, str, UUID) Proste, listy Modele Pydantic
Metody HTTP Wszystkie Wszystkie (głównie GET) POST, PUT, PATCH

Przykład kodu:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


# user_id — parametr ŚCIEŻKI (jest we wzorcu URL)
# q       — parametr ZAPYTANIA (typ prosty, brak we wzorcu)
# item    — CIAŁO żądania (model Pydantic)
@app.put("/users/{user_id}/items")
async def update_user_item(user_id: int, item: Item, q: str | None = None):
    result = {"user_id": user_id, "item": item}
    if q:
        result["q"] = q
    return result

Materiały

Jak FastAPI konwertuje i waliduje parametry na podstawie adnotacji typów?

Odpowiedź w 30 sekund: FastAPI odczytuje adnotacje typów z sygnatury funkcji i wykorzystuje Pydantic do automatycznej konwersji oraz walidacji danych wejściowych. Tekst z URL (np. "42") jest konwertowany do zadeklarowanego typu (np. int = 42), a w razie niezgodności klient otrzymuje czytelny błąd 422 Unprocessable Entity. Dzięki temu w ciele funkcji masz już gotowe, poprawne typy bez ręcznego parsowania.

Odpowiedź w 2 minuty: Sednem FastAPI jest „type-driven development": adnotacje typów to nie tylko podpowiedzi dla edytora, lecz źródło prawdy o tym, jak walidować i konwertować dane. Gdy zadeklarujesz item_id: int, FastAPI spróbuje skonwertować przychodzący fragment URL na liczbę całkowitą. Jeśli się uda, w funkcji dostaniesz int; jeśli klient poda "abc", FastAPI zwróci automatyczny błąd 422 z dokładnym wskazaniem, które pole jest nieprawidłowe i dlaczego. To „parse, don't validate" w praktyce — w środku handlera operujesz już na poprawnych, typowanych danych.

Mechanizm obsługuje wiele typów: proste (int, float, str, bool), typy złożone z typing (np. list[int]), Enum do ograniczonego zbioru wartości, daty i czasy (datetime, date), UUID, a dla ciała żądania — modele Pydantic z dowolnie zagnieżdżoną strukturą. Wartość bool jest rozpoznawana elastycznie: true, 1, on, yes (i odpowiedniki) interpretowane są jako prawda. Wszystkie te reguły FastAPI eksponuje też w automatycznej dokumentacji OpenAPI (Swagger UI), więc kontrakt API zawsze zgadza się z kodem.

Ta sama walidacja działa dla parametrów ścieżki, zapytania i ciała — różni się tylko źródło danych. Dodatkowe ograniczenia (np. zakres liczb, długość tekstu, wyrażenie regularne) dodaje się przez Query, Path i Body (opisane w osobnym pytaniu). Częsta pułapka: FastAPI konwertuje typy tam, gdzie to ma sens, ale nie „zgaduje" — jeśli oczekujesz int, a przyjdzie 3.14, dostaniesz błąd walidacji, a nie ciche obcięcie. Druga pułapka: dla Enum wartości spoza zbioru również kończą się błędem 422, co jest pożądane, bo chroni przed nieprawidłowymi danymi.

Przykład kodu:

from enum import Enum
from datetime import date
from fastapi import FastAPI

app = FastAPI()


class Status(str, Enum):
    aktywny = "aktywny"
    nieaktywny = "nieaktywny"


# item_id zostanie skonwertowany z tekstu URL na int (lub błąd 422)
# status musi należeć do zbioru wartości Enum
# created_after parsowany jest jako data ISO (np. 2026-01-31)
@app.get("/items/{item_id}")
async def read_item(item_id: int, status: Status, created_after: date | None = None):
    return {
        "item_id": item_id,        # typ: int
        "status": status.value,    # tylko dozwolone wartości
        "created_after": created_after,
    }
# GET /items/abc  -> 422, bo "abc" nie jest liczbą całkowitą
# GET /items/5?status=usuniety -> 422, wartość spoza Enum

Materiały

Jak ustawić parametry opcjonalne oraz ich wartości domyślne w endpointach?

Odpowiedź w 30 sekund: Parametr staje się opcjonalny, gdy nadasz mu wartość domyślną, np. limit: int = 10. Aby zezwolić również na brak wartości (None), użyj typu opcjonalnego: q: str | None = None. Parametry bez wartości domyślnej są wymagane — jeśli ich nie podasz, FastAPI zwróci błąd 422.

Odpowiedź w 2 minuty: W FastAPI to wartość domyślna decyduje o tym, czy parametr jest wymagany. Jeśli zadeklarujesz limit: int = 10, to przy braku tego parametru w żądaniu funkcja otrzyma 10. Jeśli chcesz, by parametr mógł być pominięty i wówczas przyjmował None, zadeklaruj go jako q: str | None = None (w Pythonie 3.10+) albo Optional[str] = None (z modułu typing, dla 3.9). Sam zapis str | None bez przypisania = None nadal czyni parametr wymaganym — różnica polega na tym, że dopuszcza on dodatkowo wartość pustą, ale klient i tak musi go podać.

Dla parametrów ścieżki sprawa jest prosta: są one zawsze wymagane, bo stanowią część adresu URL, więc nie nadaje się im wartości domyślnych. Opcjonalność dotyczy więc głównie parametrów zapytania (query) i pól ciała żądania. Dobrą praktyką jest podawanie sensownych wartości domyślnych dla paginacji (skip: int = 0, limit: int = 20) — upraszcza to korzystanie z API i czyni je bardziej odpornym na pominięcia.

W nowoczesnym FastAPI zalecaną składnią jest Annotated (opisane szerzej w osobnym pytaniu). Wówczas wartość domyślną nadal podaje się przez znak = po typie, np. q: Annotated[str | None, Query()] = None. To istotne: w stylu Annotated wartość domyślną ustawia się w przypisaniu funkcji, a NIE wewnątrz Query(default=...). Mieszanie obu sposobów (Query(default="x") i = "x" jednocześnie) jest błędem koncepcyjnym i potrafi mylić.

Częsta pułapka: nigdy nie używaj zmiennych typów mutowalnych jako wartości domyślnych w samej sygnaturze (np. tags: list = []) — to klasyczny problem współdzielonego obiektu między wywołaniami. Dla list i innych struktur w ciele żądania użyj modeli Pydantic albo Query()/Field() z default_factory. Druga pułapka: pamiętaj, że 0, "" i False to prawidłowe, „nie-puste" wartości — jeśli chcesz rozróżnić „nie podano" od „podano wartość fałszywą", użyj None jako wartości domyślnej.

Przykład kodu:

from typing import Annotated
from fastapi import FastAPI, Query

app = FastAPI()


# skip i limit mają wartości domyślne -> są OPCJONALNE
# q jest opcjonalny i może być None (gdy klient go pominie)
# required_filter NIE ma wartości domyślnej -> jest WYMAGANY
@app.get("/products")
async def list_products(
    required_filter: str,                         # wymagany
    skip: int = 0,                                # opcjonalny, domyślnie 0
    limit: int = 20,                              # opcjonalny, domyślnie 20
    q: Annotated[str | None, Query()] = None,     # opcjonalny, może być None
):
    return {
        "filter": required_filter,
        "skip": skip,
        "limit": limit,
        "q": q,
    }
# GET /products?required_filter=elektronika  -> skip=0, limit=20, q=None
# GET /products                              -> 422 (brak required_filter)

Materiały

Do czego służą Query, Path i Body oraz jak dodać do nich walidację i metadane?

Odpowiedź w 30 sekund: Query, Path i Body to funkcje pomocnicze, które pozwalają dodać do parametrów walidację (np. zakres liczb, długość tekstu, wyrażenie regularne) oraz metadane (tytuł, opis, przykład) widoczne w dokumentacji OpenAPI. W nowoczesnym FastAPI używa się ich w połączeniu z Annotated, np. q: Annotated[str, Query(min_length=3)]. Dzięki temu deklaratywnie wymuszasz reguły bez ręcznego sprawdzania danych w kodzie.

Odpowiedź w 2 minuty: Te trzy funkcje odpowiadają trzem źródłom danych: Query dla parametrów zapytania, Path dla parametrów ścieżki, Body dla pól ciała żądania (gdy chcesz wymusić, że prosty typ ma trafić do body, lub osadzić go jako klucz). Pozwalają zarówno na walidację, jak i na wzbogacenie dokumentacji. Dla tekstów dostępne są min_length, max_length oraz pattern (wyrażenie regularne; we wcześniejszych wersjach regex). Dla liczb używa się ograniczeń gt (większe niż), ge (większe lub równe), lt (mniejsze niż), le (mniejsze lub równe). Metadane to m.in. title, description, examples, deprecated, a także alias pozwalający odbierać parametr pod inną nazwą niż argument funkcji.

Zalecaną, nowoczesną składnią jest Annotated[typ, Query(...)] zamiast przekazywania Query(...) jako wartości domyślnej argumentu. Styl z Annotated ma kilka zalet: nie miesza walidacji z wartością domyślną (tę nadal podajesz po =), działa poprawnie z narzędziami statycznej analizy typów, jest reużywalny (można zdefiniować MyQuery = Annotated[str, Query(min_length=3)] i stosować w wielu miejscach) oraz nie wprowadza w błąd co do tego, czy parametr jest wymagany. Stary styl q: str = Query(default=None, min_length=3) nadal działa, ale dokumentacja FastAPI promuje Annotated.

Dla Path typowym ograniczeniem jest Path(gt=0), bo identyfikatory zwykle są dodatnie — wymusza to walidację już na wejściu i zwraca 422 przy id=0 lub ujemnym. Dla Query częste są min_length/max_length przy parametrach wyszukiwania oraz le przy limit, by ograniczyć rozmiar strony. Częsta pułapka: w stylu Annotated wartości domyślnej NIE umieszczaj w Query(default=...), tylko po = w sygnaturze — inaczej FastAPI zgłosi błąd o podwójnej wartości domyślnej. Druga pułapka: pattern/regex walidują cały string, a niezgodność kończy się błędem 422 — to dobre miejsce na walidację formatów (np. kodów pocztowych).

Przykład kodu:

from typing import Annotated
from fastapi import FastAPI, Query, Path, Body
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


@app.put("/items/{item_id}")
async def update_item(
    # Path: identyfikator musi być dodatni (gt=0), z opisem dla dokumentacji
    item_id: Annotated[int, Path(gt=0, title="ID elementu", description="Dodatni identyfikator")],
    # Query: tekst wyszukiwania o długości 3-50 znaków; domyślnie None (opcjonalny)
    q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
    # Query: limit z zakresu 1-100 (ge/le), domyślnie 20
    limit: Annotated[int, Query(ge=1, le=100)] = 20,
    # Body: pole osadzone w ciele żądania pod kluczem "item"
    item: Annotated[Item, Body(embed=True)] = None,
):
    return {"item_id": item_id, "q": q, "limit": limit, "item": item}
# PUT /items/0  -> 422 (item_id musi być > 0)
# ?q=ab         -> 422 (za krótkie, min_length=3)
# ?limit=500    -> 422 (poza zakresem, le=100)

Materiały

Jak działa APIRouter i jak organizować większą aplikację w wiele modułów?

Odpowiedź w 30 sekund: APIRouter to „mini-aplikacja" pozwalająca grupować powiązane path operations w osobnych modułach, a następnie podpinać je do głównego FastAPI przez app.include_router(...). Dzięki temu kod dzielisz na pliki tematyczne (np. users.py, items.py), a wspólne ustawienia (prefix, tags, dependencies, responses) deklarujesz raz dla całego routera. To standardowy sposób na skalowanie projektu z „Bigger Applications".

Odpowiedź w 2 minuty: W większej aplikacji trzymanie wszystkich endpointów w jednym pliku staje się niewygodne. APIRouter rozwiązuje to, działając jak osobny, mniejszy router, który ma te same dekoratory co FastAPI (@router.get, @router.post itd.). Każdy moduł domenowy definiuje własny router, a w pliku głównym (main.py) importujesz je i rejestrujesz przez app.include_router(users.router). Można też nadać wspólny prefiks i tagi w samym APIRouter(prefix="/users", tags=["users"]), dzięki czemu wszystkie ścieżki w tym module dziedziczą /users i są pogrupowane w dokumentacji.

include_router przyjmuje dodatkowe argumenty, które stosują się do wszystkich tras danego routera: prefix (wspólny przedrostek ścieżki), tags (grupowanie w Swagger UI), dependencies (np. wspólna autoryzacja dla całego modułu) oraz responses (wspólne, dodatkowe odpowiedzi w dokumentacji). Pozwala to centralnie nałożyć np. zależność sprawdzającą token na wszystkie endpointy administracyjne, bez powtarzania jej w każdej funkcji. Routery można też zagnieżdżać: jeden router może zawierać (include_router) inny, co jest przydatne przy wersjonowaniu API (np. router /api/v1 zawierający routery domenowe).

Typowa struktura projektu to katalog app/ z main.py, podkatalogiem routers/ (np. users.py, items.py), models/ (modele Pydantic i ORM), dependencies.py oraz database.py. W stylu pakietowym z dokumentacji FastAPI używa się też internal/ na funkcje administracyjne. Częsta pułapka: po wydzieleniu routerów trzeba pamiętać o ich faktycznym dołączeniu w main.py — pominięcie include_router sprawia, że endpointy „znikają" (404), mimo że kod istnieje. Druga pułapka: jeśli prefiks ustawisz i w APIRouter, i ponownie w include_router, ścieżki się zdublują (np. /users/users/...) — wybierz jedno miejsce. Trzecia: importy cykliczne — moduły routerów nie powinny importować obiektu app, lecz definiować własny router i być importowane przez main.

Przykład kodu:

# --- app/routers/users.py ---
from fastapi import APIRouter

# Wspólny prefiks i tag dla całego modułu użytkowników
router = APIRouter(prefix="/users", tags=["users"])


@router.get("")          # finalna ścieżka: GET /users
async def list_users():
    return [{"id": 1, "name": "Anna"}]


@router.get("/{user_id}")  # finalna ścieżka: GET /users/{user_id}
async def get_user(user_id: int):
    return {"id": user_id}


# --- app/main.py ---
from fastapi import FastAPI
from app.routers import users, items

app = FastAPI()

# Dołączamy routery domenowe do głównej aplikacji
app.include_router(users.router)
app.include_router(items.router)
# Można nadpisać prefiks/zależności podczas dołączania:
# app.include_router(admin.router, prefix="/admin", dependencies=[Depends(verify_admin)])

Diagram:

flowchart TD
    M["main.py — aplikacja FastAPI"]
    RU["router: users<br/>(prefix /users)"]
    RI["router: items<br/>(prefix /items)"]
    RA["router: admin<br/>(zależność: autoryzacja)"]
    M -->|include_router| RU
    M -->|include_router| RI
    M -->|include_router| RA
    RU --> EU1["GET /users"]
    RU --> EU2["GET /users/{user_id}"]
    RI --> EI1["GET /items"]
    RA --> EA1["POST /admin/notify"]

Materiały

Jak kolejność definiowania ścieżek wpływa na dopasowanie żądania (np. /users/me vs /users/{id})?

Odpowiedź w 30 sekund: FastAPI dopasowuje ścieżki w kolejności ich zadeklarowania — pierwsza pasująca trasa wygrywa. Dlatego ścieżki stałe, jak /users/me, muszą być zdefiniowane PRZED ścieżkami z parametrem, jak /users/{user_id}. W przeciwnym razie żądanie /users/me trafi do trasy /users/{user_id}, a "me" zostanie potraktowane jako wartość parametru (lub spowoduje błąd walidacji, jeśli oczekiwany jest int).

Odpowiedź w 2 minuty: Router FastAPI (oparty na Starlette) sprawdza zarejestrowane trasy po kolei i wybiera pierwszą, której wzorzec pasuje do ścieżki żądania. Trasa z parametrem /users/{user_id} pasuje do dowolnego segmentu w tym miejscu — w tym do dosłownego me. Jeśli więc zadeklarujesz ją przed /users/me, to żądanie GET /users/me zostanie obsłużone przez handler parametryczny z user_id="me". Gdy parametr jest typu int, dodatkowo dostaniesz błąd 422, bo "me" nie konwertuje się na liczbę. Rozwiązanie jest proste: definiuj trasy bardziej szczegółowe (stałe) przed bardziej ogólnymi (z parametrami).

Ta sama zasada dotyczy innych kolizji: ścieżki o wspólnym prefiksie, gdzie jeden wariant jest stały, a drugi parametryczny. Przykłady to /items/search przed /items/{item_id} czy /files/upload przed /files/{filename}. Reguła „pierwsza pasująca wygrywa" sprawia, że kolejność @app.get w pliku (lub kolejność include_router) ma realny wpływ na zachowanie API. To często zaskakuje osoby przyzwyczajone do routerów, które wybierają „najbardziej specyficzną" trasę automatycznie — FastAPI tego nie robi, decyduje kolejność.

W praktyce warto przyjąć konwencję: najpierw trasy literalne (słowa kluczowe jak me, search, count), potem trasy z parametrami. Przy podziale na routery pamiętaj, że dotyczy to także kolejności w obrębie jednego routera oraz kolejności dołączania routerów do aplikacji, jeśli ich ścieżki mogłyby kolidować. Częsta pułapka: dobranie typu parametru (int) bywa „przypadkową ochroną" — /users/me przy /users/{user_id: int} da 422 zamiast cichego błędnego dopasowania, ale to wciąż błąd, a nie poprawne działanie. Lepiej polegać na właściwej kolejności niż na typie. Druga pułapka: jeśli używasz tego samego prefiksu w wielu routerach, kolizje mogą wystąpić między modułami — wtedy kolejność include_router przesądza, który handler obsłuży żądanie.

Przykład kodu:

from fastapi import FastAPI

app = FastAPI()


# DOBRZE: trasa stała PRZED trasą parametryczną
@app.get("/users/me")           # 1) najpierw ścieżka literalna
async def read_current_user():
    return {"user": "aktualnie zalogowany"}


@app.get("/users/{user_id}")    # 2) potem ścieżka z parametrem
async def read_user(user_id: int):
    return {"user_id": user_id}


# ŹLE (gdyby kolejność była odwrotna):
# @app.get("/users/{user_id}")  # ta trasa przechwyciłaby /users/me
# @app.get("/users/me")         # nigdy nie zostałaby osiągnięta
#
# GET /users/me  -> przy złej kolejności trafiłoby do {user_id}="me"
#                   i dało 422, bo "me" nie jest liczbą int

Diagram:

flowchart TD
    R["Żądanie: GET /users/me"]
    R --> C1{"Pasuje /users/me?"}
    C1 -->|"Tak (zadeklarowane pierwsze)"| H1["Handler read_current_user"]
    C1 -->|"Nie sprawdzane, gdy /me jest pierwsze"| C2{"Pasuje /users/{user_id}?"}
    C2 -->|"Tak"| H2["Handler read_user — user_id='me' -> 422"]

Materiały

Pydantic i modele danych

Czym jest Pydantic i jak FastAPI go wykorzystuje?

Odpowiedź w 30 sekund: Pydantic to biblioteka do walidacji danych i zarządzania ustawieniami w Pythonie, oparta na adnotacjach typów. FastAPI używa jej jako fundamentu: na podstawie typów w sygnaturach funkcji i modelach automatycznie waliduje dane wejściowe, konwertuje typy, serializuje odpowiedzi i generuje dokumentację OpenAPI.

Odpowiedź w 2 minuty: Pydantic pozwala opisać kształt danych za pomocą zwykłych podpowiedzi typów (type hints), a następnie sam waliduje i parsuje dane wejściowe do tych typów. Jeśli przyjdzie JSON z polem age jako tekst "30", Pydantic spróbuje przekonwertować je do int zgodnie z deklaracją, a jeśli się nie uda — zgłosi czytelny błąd ze ścieżką do pola. Dzięki temu logika biznesowa pracuje już na poprawnie otypowanych obiektach, a nie na surowych słownikach.

FastAPI jest zbudowany wokół Pydantic. Kiedy deklarujesz parametr funkcji obsługującej endpoint jako model dziedziczący po BaseModel, FastAPI automatycznie: odczytuje ciało żądania (request body), waliduje je względem modelu, zwraca błąd 422 Unprocessable Entity przy niezgodności oraz buduje schemat JSON Schema do interaktywnej dokumentacji (Swagger UI, ReDoc). To samo dotyczy odpowiedzi — dzięki response_model FastAPI serializuje i filtruje dane wyjściowe.

Warto rozumieć podział ról: FastAPI to warstwa webowa (routing, HTTP, zależności), a Pydantic to warstwa danych (walidacja, parsowanie, serializacja). Najczęstsza pułapka początkujących to mylenie tych dwóch światów — np. próba walidacji logiki HTTP wewnątrz walidatora pola albo zakładanie, że Pydantic „magicznie" sprawdzi reguły biznesowe, których nie zadeklarowano. Pydantic v2 ma rdzeń napisany w Rust (pydantic-core), co czyni walidację bardzo szybką.

Przykład kodu:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

# Model danych opisany zwykłymi adnotacjami typów
class Uzytkownik(BaseModel):
    imie: str
    wiek: int  # "30" zostanie przekonwertowane na int 30

@app.post("/uzytkownicy")
def utworz_uzytkownika(uzytkownik: Uzytkownik):
    # W tym miejscu dane są już zwalidowane i otypowane
    return {"powitanie": f"Cześć, {uzytkownik.imie}!"}

Materiały

Jak definiuje się model żądania przy użyciu klasy BaseModel?

Odpowiedź w 30 sekund: Tworzysz klasę dziedziczącą po BaseModel i deklarujesz pola jako adnotacje typów (np. nazwa: str, cena: float). Pole bez wartości domyślnej jest wymagane, a z wartością domyślną — opcjonalne. W FastAPI wystarczy użyć takiej klasy jako typu parametru funkcji, by stała się ciałem żądania (request body).

Odpowiedź w 2 minuty: BaseModel to bazowa klasa Pydantic. Definiując model, podajesz nazwy pól wraz z ich typami. Pydantic na tej podstawie generuje konstruktor, walidację, serializację oraz schemat. Pole bez wartości domyślnej jest wymagane — jego brak w danych wejściowych spowoduje błąd walidacji. Pole z wartością domyślną (np. aktywny: bool = True) jest opcjonalne. Pola opcjonalne, które mogą przyjmować None, deklaruje się jako Optional[str] lub nowocześniej str | None, zwykle z domyślnym = None.

W FastAPI, gdy parametr funkcji endpointu ma typ będący podklasą BaseModel, framework rozpoznaje go jako ciało żądania w formacie JSON. Odczyta ciało, zwaliduje je względem modelu i przekaże gotowy obiekt. Do dostępu do danych używasz atrybutów obiektu (produkt.nazwa), a do zamiany na słownik lub JSON — metod Pydantic v2: model_dump() (słownik) i model_dump_json() (tekst JSON). Stare metody .dict() i .json() z v1 są w v2 oznaczone jako przestarzałe (deprecated).

Najgroźniejsza pułapka to mutowalne wartości domyślne. Nie wolno pisać tagi: list[str] = [], bo choć Pydantic obsługuje to bezpieczniej niż czysty Python, intencję najlepiej wyrazić przez Field(default_factory=list). Druga pułapka: zapominanie, że Pydantic domyślnie konwertuje typy (np. "30" → 30), więc jeśli zależy ci na ścisłym typie, użyj trybu strict. Trzecia: nadmiarowe pola w danych wejściowych są domyślnie ignorowane — jeśli chcesz je odrzucać, ustaw to w model_config.

Przykład kodu:

from pydantic import BaseModel, Field

class Produkt(BaseModel):
    nazwa: str                       # pole wymagane
    cena: float                      # pole wymagane
    opis: str | None = None          # pole opcjonalne, może być None
    aktywny: bool = True             # pole opcjonalne z wartością domyślną
    tagi: list[str] = Field(default_factory=list)  # poprawna lista domyślna

# Utworzenie i walidacja z danych zewnętrznych
produkt = Produkt.model_validate({"nazwa": "Klawiatura", "cena": "199.99"})
print(produkt.cena)            # 199.99 (str przekonwertowany na float)
print(produkt.model_dump())    # słownik gotowy do dalszego użycia

Materiały

Do czego służy Field i jakie ograniczenia walidacyjne można w nim ustawić?

Odpowiedź w 30 sekund: Field to funkcja Pydantic do precyzyjnego konfigurowania pojedynczego pola: wartości domyślnej, opisu, aliasu oraz ograniczeń walidacyjnych. Pozwala ustawić m.in. zakresy liczbowe (gt, ge, lt, le), długość tekstu i kolekcji (min_length, max_length) czy wzorzec wyrażenia regularnego (pattern).

Odpowiedź w 2 minuty: Field używasz wtedy, gdy sama adnotacja typu to za mało i chcesz dodać metadane lub reguły. Najczęstsze ograniczenia liczbowe to gt (większe niż), ge (większe lub równe), lt (mniejsze niż), le (mniejsze lub równe) oraz multiple_of. Dla tekstów i kolekcji dostępne są min_length i max_length, a dla łańcuchów również pattern (wyrażenie regularne). Pierwszy argument Field to wartość domyślna — Field(...) (wielokropek, Ellipsis) oznacza pole wymagane, a Field(default_factory=...) służy do bezpiecznych domyślnych kolekcji.

Field przyjmuje też metadane dla dokumentacji: description, title, examples. FastAPI przenosi je do schematu OpenAPI, więc pojawiają się w Swagger UI — to ważny element samodokumentującego się API. Przydatny jest alias (np. gdy zewnętrzny JSON używa camelCase, a w Pythonie chcesz snake_case) oraz frozen=True dla pól tylko do odczytu. W v2 metadane można też składać przez Annotated[int, Field(gt=0)], co jest zalecanym, czytelnym stylem.

Pułapki: po pierwsze, Field waliduje strukturę i proste reguły, ale złożona logika międzypolowa należy do walidatorów (field_validator, model_validator) — nie próbuj wciskać jej w Field. Po drugie, ograniczenia działają tylko na etapie walidacji wejścia (lub przy model_validate), a nie automatycznie przy każdym przypisaniu atrybutu — chyba że włączysz validate_assignment=True w model_config. Po trzecie, łatwo pomylić walidację (sprawdzanie danych wejściowych) z serializacją (zamianą obiektu na JSON) — Field wpływa głównie na wejście i na schemat.

Przykład kodu:

from typing import Annotated
from pydantic import BaseModel, Field

class ZamowieniePozycja(BaseModel):
    # Pole wymagane z opisem i ograniczeniem długości
    nazwa: str = Field(..., min_length=1, max_length=50,
                       description="Nazwa produktu widoczna w API")
    # Liczba dodatnia (większa od zera)
    ilosc: int = Field(..., gt=0, description="Liczba sztuk")
    # Cena: zakres i przykład w dokumentacji OpenAPI
    cena: float = Field(..., gt=0, le=100_000, examples=[199.99])
    # Styl Annotated — zalecany w Pydantic v2
    kod: Annotated[str, Field(pattern=r"^[A-Z]{3}-\d{4}$")]

Materiały

Jakie są najważniejsze różnice między Pydantic v1 a v2 i jak wpłynęły na FastAPI?

Odpowiedź w 30 sekund: Pydantic v2 przepisał rdzeń walidacji w Rust (pydantic-core), dając kilkukrotnie wyższą wydajność. Zmienił też API: @validator/@root_validator ustąpiły miejsca field_validator/model_validator, wewnętrzna klasa Config została zastąpiona przez model_config = ConfigDict(...), a metody .dict()/.json()/.parse_obj() przez model_dump()/model_dump_json()/model_validate(). FastAPI w nowszych wersjach w pełni wspiera v2.

Odpowiedź w 2 minuty: Najważniejsza zmiana to wydajność — rdzeń walidacji przeniesiono do skompilowanego modułu w Rust, więc parsowanie i walidacja są znacznie szybsze niż w czysto pythonowym v1. Druga zmiana to ujednolicenie i uporządkowanie API. Walidatory pól deklaruje się dekoratorem @field_validator, a walidatory całego modelu — @model_validator (z trybami mode="before" i mode="after"). Stare @validator i @root_validator nadal działają w trybie zgodności, ale są przestarzałe i należy ich unikać w nowym kodzie.

Konfigurację modelu zamiast wewnętrznej klasy class Config: ustawia się teraz przez atrybut model_config = ConfigDict(...) — czytelniej i z lepszym wsparciem typowania. Zmieniła się też nomenklatura metod: tworzenie obiektu z danych to model_validate() (dawniej parse_obj) i model_validate_json(), a eksport to model_dump() (dawniej .dict()) i model_dump_json() (dawniej .json()). Doszły też nowe mechanizmy: computed_field, lepsze typy Annotated, oddzielenie logiki walidacji od serializacji oraz TypeAdapter do walidacji typów spoza BaseModel.

Dla FastAPI oznaczało to istotny krok: FastAPI od wersji 0.100 wspiera Pydantic v2, co przełożyło się na szybszą walidację żądań i odpowiedzi oraz dokładniejszy JSON Schema (zgodny z nowszą specyfikacją OpenAPI). Praktyczne pułapki podczas migracji: mieszanie API v1 i v2 w jednym projekcie (np. @validator obok field_validator), używanie wycofanych metod, oraz subtelne zmiany domyślnych zachowań walidacji (np. ściślejsze parsowanie niektórych typów). Pydantic udostępnia podmoduł pydantic.v1 do stopniowej migracji, a narzędzie bump-pydantic pomaga zautomatyzować część zmian.

Diagram:

Aspekt Pydantic v1 Pydantic v2
Rdzeń walidacji Czysty Python pydantic-core w Rust (szybszy)
Walidator pola @validator (przestarzały) @field_validator
Walidator modelu @root_validator (przestarzały) @model_validator (mode before/after)
Konfiguracja wewnętrzna klasa Config model_config = ConfigDict(...)
Tworzenie z danych parse_obj() model_validate() / model_validate_json()
Eksport do słownika .dict() model_dump()
Eksport do JSON .json() model_dump_json()
Wsparcie w FastAPI starsze wersje od FastAPI 0.100+

Materiały

Jak tworzyć własne walidatory pól i modeli (field_validator, model_validator)?

Odpowiedź w 30 sekund: Do walidacji pojedynczego pola używasz dekoratora @field_validator("nazwa_pola") na metodzie klasy (jako @classmethod), która sprawdza i ewentualnie przekształca wartość. Do reguł obejmujących wiele pól (np. „data końca musi być po dacie startu") używasz @model_validator(mode="after"), który ma dostęp do całego, już zwalidowanego obiektu.

Odpowiedź w 2 minuty: field_validator w Pydantic v2 stosujesz, gdy proste ograniczenia z Field nie wystarczają — np. chcesz znormalizować tekst (usunąć spacje, zmienić wielkość liter) albo sprawdzić regułę domenową dla jednego pola. Metoda musi być @classmethod, przyjmuje wartość i powinna zwrócić zwalidowaną (ewentualnie zmienioną) wartość. Tryb mode="before" uruchamia walidator przed standardową konwersją typu (dostajesz surowe dane), a mode="after" po niej (dostajesz wartość już w docelowym typie). Błąd zgłaszasz, podnosząc ValueError — Pydantic zamieni go na czytelny komunikat walidacji, a FastAPI na odpowiedź 422.

model_validator działa na poziomie całego modelu i jest właściwym miejscem na logikę międzypolową. W trybie mode="after" metoda jest zwykłą (nie classmethod), dostaje self z kompletnym, zwalidowanym obiektem i może porównywać pola między sobą (np. sprawdzić, że haslo == haslo_powtorz). W trybie mode="before" dostaje surowe dane wejściowe (zwykle słownik) i może je przekształcić, zanim powstaną pola — przydatne do normalizacji lub łączenia pól.

Najważniejsze pułapki: po pierwsze, w v2 to field_validator/model_validator, a nie @validator/@root_validator z v1 — mieszanie API to częsty błąd przy migracji. Po drugie, walidator pola widzi tylko swoje pole (nie ma dostępu do pozostałych) — logika międzypolowa należy do model_validator. Po trzecie, walidatory służą do walidacji wejścia, a nie do formatowania wyjścia — do modyfikacji danych przy serializacji służą field_serializer/model_serializer, więc nie myl walidacji z serializacją. Po czwarte, pamiętaj o @classmethod przy field_validator — jego brak to typowy błąd.

Przykład kodu:

from pydantic import BaseModel, field_validator, model_validator

class Rejestracja(BaseModel):
    email: str
    haslo: str
    haslo_powtorz: str

    @field_validator("email")
    @classmethod
    def normalizuj_email(cls, wartosc: str) -> str:
        # Walidacja i normalizacja pojedynczego pola
        wartosc = wartosc.strip().lower()
        if "@" not in wartosc:
            raise ValueError("Niepoprawny adres e-mail")
        return wartosc

    @model_validator(mode="after")
    def hasla_zgodne(self) -> "Rejestracja":
        # Reguła międzypolowa — dostęp do całego obiektu
        if self.haslo != self.haslo_powtorz:
            raise ValueError("Hasła muszą być identyczne")
        return self

Materiały

Jak obsługiwać modele zagnieżdżone oraz listy modeli?

Odpowiedź w 30 sekund: Model może mieć pole, którego typem jest inny model BaseModel — Pydantic zwaliduje wtedy zagnieżdżoną strukturę rekurencyjnie. Listy modeli deklarujesz jako list[Model], a Pydantic sprawdzi każdy element osobno. FastAPI obsługuje takie struktury automatycznie i odwzorowuje je w schemacie OpenAPI.

Odpowiedź w 2 minuty: Zagnieżdżenie polega na tym, że pole jednego modelu ma jako typ inny model. Jeśli Zamowienie ma pole adres: Adres, Pydantic, walidując zamówienie, zwaliduje też zagnieżdżony adres — z wszystkimi jego polami i regułami. To pozwala odwzorować realne, hierarchiczne struktury danych (np. zamówienie zawiera adres dostawy i listę pozycji). Walidacja jest rekurencyjna i głęboka: błąd w dowolnym zagnieżdżonym polu zostanie zgłoszony z pełną ścieżką (np. pozycje.0.cena), co bardzo ułatwia diagnozę.

Kolekcje modeli deklarujesz typami generycznymi: list[Item] dla listy, dict[str, Item] dla słownika. Pydantic zwaliduje każdy element. Te same zasady obowiązują w FastAPI — wystarczy, że model najwyższego poziomu jest typem ciała żądania, a cała struktura (modele zagnieżdżone i listy) zostanie odczytana z JSON-a, zwalidowana i przekazana jako gotowy obiekt. W odpowiedzi działa to symetrycznie przez response_model. Do eksportu całej struktury na słownik czy JSON używasz model_dump() / model_dump_json(), które schodzą rekurencyjnie.

Pułapki: po pierwsze, mutowalne wartości domyślne dla list zagnieżdżonych modeli — używaj Field(default_factory=list), nie = []. Po drugie, przy bardzo głębokich lub rekurencyjnych strukturach (model odwołujący się do samego siebie) potrzebne bywa zadeklarowanie typu jako tekstu i wywołanie model_rebuild(). Po trzecie, łatwo pomylić walidację z serializacją — model zagnieżdżony zwaliduje dane wejściowe, ale o tym, które pola znajdą się w odpowiedzi, decyduje response_model i opcje model_dump() (np. exclude_none=True). Po czwarte, walidacja głębokich list ma swój koszt — przy dużych payloadach warto rozważyć limity (max_length).

Przykład kodu:

from pydantic import BaseModel, Field

class Adres(BaseModel):
    ulica: str
    miasto: str

class Pozycja(BaseModel):
    nazwa: str
    ilosc: int = Field(..., gt=0)

class Zamowienie(BaseModel):
    numer: str
    adres: Adres                 # model zagnieżdżony
    pozycje: list[Pozycja] = Field(default_factory=list)  # lista modeli

# Cała struktura walidowana rekurencyjnie z jednego słownika/JSON-a
zamowienie = Zamowienie.model_validate({
    "numer": "ZAM-2026-001",
    "adres": {"ulica": "Marszałkowska 1", "miasto": "Warszawa"},
    "pozycje": [{"nazwa": "Mysz", "ilosc": 2}],
})

Diagram:

classDiagram
    class Zamowienie {
        +str numer
        +Adres adres
        +list~Pozycja~ pozycje
    }
    class Adres {
        +str ulica
        +str miasto
    }
    class Pozycja {
        +str nazwa
        +int ilosc
    }
    Zamowienie "1" --> "1" Adres : zawiera
    Zamowienie "1" --> "*" Pozycja : zawiera listę

Materiały

21. Czym różni się BaseModel od dataclass w kontekście FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Walidacja, serializacja i odpowiedzi

22. Do czego służy parametr response_model i jakie daje korzyści?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
23. Dlaczego warto rozdzielać modele wejścia i wyjścia (np. UserIn vs UserOut)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
24. Jak ustawić kod statusu HTTP odpowiedzi (status_code)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
25. Jak obsłużyć przesyłanie plików (File, UploadFile) oraz dane formularzy (Form)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
26. Jak zwracać niestandardowe odpowiedzi (JSONResponse, RedirectResponse, StreamingResponse)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
27. Do czego służą opcje response_model_exclude_unset, exclude_none i podobne?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Wstrzykiwanie zależności (Depends)

28. Czym jest system Dependency Injection w FastAPI i jakie problemy rozwiązuje?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
29. Jak działa Depends i jak definiować zależności funkcyjne?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
30. Czym są zależności z yield i do czego służą (np. zarządzanie sesją bazy danych)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
31. Jak działa cache'owanie zależności w obrębie jednego żądania (use_cache)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
32. Jak budować zależności zagnieżdżone (sub-dependencies) oraz zależności klasowe?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
33. Jak stosować zależności na poziomie routera lub całej aplikacji (global dependencies)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Programowanie asynchroniczne

34. Jaka jest różnica między def a async def w definicji endpointu?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
35. Kiedy używać async def, a kiedy zwykłego def w FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
36. Co się dzieje, gdy w async endpoincie wywołasz operację blokującą (np. blokujące I/O)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
37. Jak FastAPI obsługuje współbieżność i czym różni się ona od równoległości?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
38. Jak uruchamiać zadania w tle (BackgroundTasks) i jakie mają ograniczenia?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
39. Jak wywołać kod synchroniczny lub blokujące biblioteki z async endpointu bez blokowania event loopa?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Uwierzytelnianie i bezpieczeństwo

40. Jakie narzędzia bezpieczeństwa (security utilities) oferuje FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
41. Jak zaimplementować uwierzytelnianie OAuth2 z tokenem (OAuth2PasswordBearer)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
42. Jak działa uwierzytelnianie oparte na JWT i jak je wdrożyć w FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
43. Czym są scopes w OAuth2 i jak realizować autoryzację opartą na uprawnieniach?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
44. Jak skonfigurować CORS w FastAPI (CORSMiddleware)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
45. Jakie są dobre praktyki bezpieczeństwa API w FastAPI (haszowanie haseł, HTTPS, sekrety, rate limiting)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Baza danych i integracja z ORM

46. Jak zintegrować FastAPI z bazą danych przy użyciu SQLAlchemy?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
47. Jak dostarczać sesję bazy danych przez system zależności (Depends + yield)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
48. Czym jest SQLModel i jak upraszcza pracę z bazą w FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
49. Jak korzystać z asynchronicznego dostępu do bazy (async SQLAlchemy, sterowniki async)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
50. Jak zarządzać migracjami schematu (np. Alembic) w projekcie FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Obsługa błędów i middleware

51. Jak zgłaszać błędy HTTP za pomocą HTTPException?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
52. Jak tworzyć własne handlery wyjątków (exception_handler) i globalnie obsługiwać błędy?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
53. Co FastAPI zwraca przy błędzie walidacji wejścia (RequestValidationError, status 422)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
54. Czym jest middleware w FastAPI i jak napisać własny?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
55. Jak obsługiwać zdarzenia cyklu życia aplikacji (startup/shutdown, lifespan)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Dokumentacja API (OpenAPI)

56. Jak FastAPI automatycznie generuje dokumentację OpenAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
57. Czym różnią się Swagger UI i ReDoc oraz jak uzyskać do nich dostęp?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
58. Jak wzbogacić dokumentację (tags, summary, description, examples, metadane aplikacji)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
59. Jak dostosować lub wyłączyć automatyczną dokumentację oraz schemat OpenAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Testowanie

60. Jak testować aplikację FastAPI za pomocą TestClient?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
61. Jak nadpisywać zależności w testach (dependency_overrides)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
62. Jak testować endpointy asynchroniczne (httpx.AsyncClient, pytest-asyncio)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
63. Jak testować uwierzytelnianie i chronione endpointy?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
64. Jakie są dobre praktyki organizacji testów w projekcie FastAPI (fixtures, testowa baza danych)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Wydajność, wdrożenie i produkcja

65. Jak wdrożyć aplikację FastAPI na produkcję (uvicorn, gunicorn, workery)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
66. Jaka jest rola serwera ASGI (uvicorn) oraz menedżera procesów (gunicorn) podczas wdrożenia?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
67. Jak skonteneryzować aplikację FastAPI w Dockerze zgodnie z dobrymi praktykami?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
68. Jak obsługiwać WebSockets w FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
69. Jak diagnozować i optymalizować wydajność aplikacji FastAPI?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi
70. Jak zarządzać konfiguracją i ustawieniami aplikacji (pydantic-settings, zmienne środowiskowe)?

Odpowiedź dostępna w pełnej wersji. Odblokuj 50 pozostałych odpowiedzi i ucz się z pełnego zestawu.

Odblokuj odpowiedzi

Chcesz poznać wszystkie odpowiedzi?

Uzyskaj pełny dostęp do 70 pytań rekrutacyjnych z FastAPI oraz pozostałych technologii.

Zobacz plany cenowe