Najpopularniejsze pytania rekrutacyjne z FastAPI (z odpowiedziami)

Piotr Dul

07-11-2026

Pydantic i walidacja, async vs def, Depends, response_model, uwierzytelnianie i testy. Sprawdź, jakie pytania z FastAPI padają na rozmowach na juniora i jak na nie odpowiadać.

Najpopularniejsze pytania rekrutacyjne z FastAPI (z odpowiedziami)
Na rozmowach z FastAPI najczęściej pada kilkanaście pytań: czym FastAPI różni się od Flaska i Django, jak działa walidacja przez Pydantic, czym różni się async def od zwykłego def, czym jest Depends i response_model oraz jak się je testuje. Rekruter nie sprawdza, czy znasz całą dokumentację, tylko czy rozumiesz, co dzieje się z żądaniem od przyjścia do odpowiedzi. Dobra odpowiedź to jedno zdanie definicji i krótki przykład kodu.

FastAPI szybko stał się jednym z najczęściej wybieranych frameworków do budowy API w Pythonie, więc pojawia się w ofertach na stanowisko juniora coraz częściej. Rozmowa o nim rzadko jest egzaminem z dokumentacji. Najczęściej to te same kilkanaście tematów, przy których rekruter sprawdza, czy rozumiesz, jak framework obsługuje żądanie, a nie czy pamiętasz nazwy dekoratorów. Poniżej znajdziesz pytania, które wracają na rozmowach, razem z tym, jak na nie odpowiadać. Jeśli dopiero zaczynasz, zacznij od artykułu o tym, czym jest FastAPI i jak stworzyć pierwsze API, a ten tekst potraktuj jako listę kontrolną. Sam język sprawdzimy w pytaniach rekrutacyjnych z Pythona, a framework o innej filozofii w artykule o pytaniach z Django. Mikroframework o podobnym przeznaczeniu opisujemy w tekście o pytaniach z Flaska.

Przykłady z tego artykułu sprawdziliśmy w działającej aplikacji na FastAPI 0.162 z Pydantic 2.14 i Pythonem 3.13: testowaliśmy walidację, zależności, uwierzytelnianie, CORS, zadania w tle i zachowanie przy blokujących funkcjach. Wyniki w tekście, np. czasy odpowiedzi i kody statusu, pochodzą z tych uruchomień.

Jak odpowiadać na pytania rekrutacyjne z FastAPI?

Najlepiej w trzech krokach: jedno zdanie definicji, krótki przykład (kilka linijek kodu albo sytuacja z projektu) i informacja, kiedy to ma znaczenie w praktyce. Taka odpowiedź zajmuje 30–60 sekund i pokazuje, że rozumiesz mechanizm, a nie wkułeś formułkę. Jeśli czegoś nie wiesz, powiedz, co wiesz, i rozumuj na głos. Rekruterzy oceniają sposób myślenia, a nie tylko wynik.

Pytania są ułożone od podstaw do tematów, które częściej pojawiają się przy ofertach na mid-poziom.

Podstawy FastAPI

Czym jest FastAPI i czym różni się od Flaska i Django?

FastAPI to nowoczesny framework do budowy API w Pythonie, oparty na adnotacjach typów (type hints), bibliotece Pydantic do walidacji danych i na asynchronicznym standardzie ASGI. Flask jest minimalistyczny i nie narzuca niczego poza routingiem, a Django to duży framework „z bateriami” z ORM, panelem administracyjnym i szablonami. FastAPI leży pośrodku: daje walidację, dokumentację API i wstrzykiwanie zależności od razu, ale bazę danych i resztę dobierasz sam. Szersze porównanie znajdziesz w artykule Django, Flask czy FastAPI.

Dlaczego FastAPI uchodzi za szybki?

Z dwóch powodów: działa na asynchronicznym serwerze (np. Uvicorn) i nie traci czasu na czekanie przy operacjach wejścia-wyjścia, a walidacja przez Pydantic jest zaimplementowana w szybkim rdzeniu. Nie oznacza to jednak, że każdy endpoint jest szybszy niż we Flasku. Przewaga pojawia się tam, gdzie aplikacja dużo czeka (baza, inne API), i wymaga poprawnego użycia async, o czym niżej.

Skąd FastAPI bierze dokumentację API?

Generuje ją automatycznie ze schematu OpenAPI, który tworzy na podstawie Twoich tras, adnotacji typów i modeli Pydantic. W teście /docs (Swagger UI), /redoc i /openapi.json zwróciły status 200, a ścieżka /users była w schemacie bez żadnej dodatkowej konfiguracji. To dlatego opis parametrów i typów trzeba utrzymywać w kodzie, a nie w osobnym pliku.

Jak uruchomić aplikację FastAPI?

Aplikacja to zwykły obiekt FastAPI(), który uruchamia serwer ASGI. W pracy lokalnej używa się polecenia fastapi dev (z automatycznym przeładowaniem), a na produkcji serwera Uvicorn, często z kilkoma procesami roboczymi (workers).

from fastapi import FastAPI

app = FastAPI()

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

Skąd bierze się błąd „Error loading ASGI app. Could not import module”?

To jeden z najczęściej zgłaszanych błędów początkujących. Polecenie uvicorn nazwa_modułu:nazwa_obiektu musi wskazywać plik Pythona i zmienną z aplikacją. W teście uvicorn api:app w katalogu bez pliku api.py zakończyło się komunikatem Error loading ASGI app. Could not import module "api". Sprawdź więc trzy rzeczy: czy uruchamiasz polecenie z właściwego katalogu, czy plik nazywa się tak, jak podałeś, i czy zmienna z aplikacją ma nazwę z drugiej części (app).

Parametry, walidacja i Pydantic

Czym różni się parametr ścieżki od parametru zapytania i od ciała żądania?

RodzajSkąd pochodziJak deklarujesz
Parametr ścieżki (path)część adresu: /users/5nazwa w ścieżce i argument funkcji: user_id: int
Parametr zapytania (query)po znaku ?: /items?limit=5argument funkcji spoza ścieżki: limit: int = 10
Ciało żądania (body)JSON w treści POST/PUTargument typu modelu Pydantic: user: UserIn

FastAPI rozpoznaje rodzaj sam, po tym, czy nazwa występuje w ścieżce i jakiego typu jest argument.

Jak działa walidacja danych i co dostaje klient, gdy dane są błędne?

FastAPI sprawdza typy i ograniczenia z modeli Pydantic, zanim wywoła funkcję. Przy błędzie zwraca kod 422 z listą problemów. W teście żądanie z imieniem „J”, wiekiem 0 i brakiem hasła dało 422 z trzema błędami: string_too_short dla name, greater_than dla age i missing dla password, każdy z dokładną lokalizacją (loc). Podobnie /users/abc przy user_id: int oraz limit=500 przy Query(le=100) skończyły się kodem 422. Szczegóły modeli wejścia i wyjścia opisujemy w artykule o walidacji danych w FastAPI z Pydantic.

from pydantic import BaseModel, Field

class UserIn(BaseModel):
    name: str = Field(min_length=2)
    age: int = Field(gt=0)
    password: str

Do czego służy response_model?

Określa kształt odpowiedzi: FastAPI filtruje zwracane dane do pól modelu i dokumentuje je w OpenAPI. Najważniejsze zastosowanie to ochrona przed wyciekiem. W teście ta sama funkcja zwracała słownik z hasłem: z response_model=UserOut klient dostał tylko id i name, a bez niego także password. Dlatego modele wejścia i wyjścia warto rozdzielać.

class UserOut(BaseModel):
    id: int
    name: str

@app.post("/users", response_model=UserOut, status_code=201)
def create_user(user: UserIn):
    return {"id": 1, **user.model_dump()}

Jak zwrócić własny kod statusu i błąd?

Kod sukcesu ustawiasz w dekoratorze (status_code=201), a błąd zgłaszasz przez HTTPException. W teście HTTPException(status_code=404, detail="Nie ma takiego użytkownika") zwróciło 404 z ciałem {"detail": "Nie ma takiego użytkownika"}.

from fastapi import HTTPException

@app.get("/users/{user_id}")
def get_user(user_id: int):
    if user_id != 1:
        raise HTTPException(status_code=404, detail="Nie ma takiego użytkownika")
    return {"id": user_id}

Skąd bierze się odpowiedź 307 Temporary Redirect?

Pojawia się, gdy trasa jest zdefiniowana z ukośnikiem na końcu (/items/), a klient wywołuje adres bez niego (/items). FastAPI odpowiada wtedy przekierowaniem na wersję z ukośnikiem. W teście /items zwróciło 307 z nagłówkiem location wskazującym /items/, a /items/ zwróciło 200. Spójność jest ważna: wybierz jedną formę w trasach i w kliencie. Na Stack Overflow ten problem zgłaszany jest często razem z serwerem za proxy, gdzie przekierowanie wskazuje zły adres.

Czy kolejność tras ma znaczenie?

Tak. FastAPI dopasowuje trasy w kolejności ich definicji, więc trasa ogólna (/users/{user_id}) zdefiniowana przed szczegółową (/users/me) przejmie żądanie, a me zostanie potraktowane jako user_id. W naszej aplikacji /users/me było zdefiniowane wcześniej i zwróciło własną odpowiedź. Kolejność odwrotną opisujemy z dokumentacji, nie testowaliśmy jej.

Asynchroniczność

Czym różni się async def od zwykłego def w endpoincie?

Endpoint async def działa bezpośrednio w pętli zdarzeń (event loop) i nie wolno w nim wołać funkcji blokujących. Zwykły def FastAPI uruchamia w osobnym wątku z puli, więc blokujące wywołanie nie zatrzymuje reszty aplikacji. Zasada: async def używaj, gdy w środku jest await (asynchroniczna baza, klient HTTP), w pozostałych przypadkach zwykły def.

Co się stanie, jeśli w async def wywołam funkcję blokującą?

Zablokujesz całą pętlę zdarzeń, więc inne żądania muszą czekać. Pokazuje to test z dwoma równoległymi żądaniami do endpointu, który „pracuje” 0,5 s:

EndpointCzas dwóch równoległych żądań
async def z time.sleep(0.5)1,0 s (żądania wykonały się po kolei)
zwykłe def z time.sleep(0.5)0,5 s (osobne wątki)
async def z await asyncio.sleep(0.5)0,5 s (pętla nie była zablokowana)

To klasyczny błąd juniorów: async def wygląda na „szybszy”, a ze zwykłym requests.get albo time.sleep w środku jest wolniejszy niż zwykły def. Praktyczne zastosowanie async w aplikacjach z modelami językowymi opisujemy w artykule o FastAPI, async i streamingu LLM.

import asyncio

@app.get("/ok")
async def ok():
    await asyncio.sleep(0.5)   # nie blokuje pętli zdarzeń
    return {"ok": True}

@app.get("/blocking")
def blocking():
    import time
    time.sleep(0.5)            # zwykły def: osobny wątek
    return {"ok": True}

Zależności i bezpieczeństwo

Czym jest Depends i po co wstrzykiwanie zależności?

Depends to mechanizm wstrzykiwania zależności (dependency injection): wskazujesz funkcję, a FastAPI wywoła ją przed endpointem i poda wynik jako argument. Dzięki temu logikę wspólną dla wielu tras (sesja bazy, aktualny użytkownik, paginacja) trzymasz w jednym miejscu, a zależności możesz podmienić w testach.

from typing import Annotated
from fastapi import Depends

def get_db():
    db = "połączenie"
    try:
        yield db
    finally:
        pass  # tu zamykasz połączenie

@app.get("/items")
def read_items(db: Annotated[str, Depends(get_db)]):
    return {"db": db}

Do czego służy yield w zależności?

Kod przed yield wykona się przed endpointem, a kod po yield (w finally) po jego zakończeniu, więc idealnie nadaje się do otwierania i zamykania zasobów, np. sesji bazy. W teście kolejność wywołań to open, handler, close. O tym, jak ten wzorzec wygląda z prawdziwą bazą, piszemy w artykule o SQLAlchemy 2 z FastAPI.

Jak podmienić zależność w testach?

Przez app.dependency_overrides: w teście wskazujesz funkcję zastępczą, a po teście czyścisz słownik. W naszym teście podmiana get_db na lambda: "fake-db" sprawiła, że endpoint dostał nową wartość bez żadnych zmian w kodzie aplikacji.

from fastapi.testclient import TestClient

app.dependency_overrides[get_db] = lambda: "fake-db"
client = TestClient(app)
assert client.get("/items").json() == {"db": "fake-db"}
app.dependency_overrides.clear()

Jak działa uwierzytelnianie w FastAPI?

FastAPI dostarcza narzędzia, np. OAuth2PasswordBearer, które wyciągają token z nagłówka Authorization: Bearer …. Sama weryfikacja tokenu (np. JWT) i sprawdzenie użytkownika to Twój kod w zależności. W teście brak nagłówka dał 401 z komunikatem Not authenticated, a z nagłówkiem Bearer abc token trafił do funkcji. Pełny przykład z logowaniem i JWT znajdziesz w artykule o uwierzytelnianiu w FastAPI, a o tym, żeby użytkownik widział tylko swoje dane, w tekście o autoryzacji w FastAPI.

Czym jest CORS i jak go skonfigurować?

CORS to mechanizm przeglądarki, który blokuje wywołania API z innej domeny, jeśli serwer nie zezwoli na nie nagłówkiem Access-Control-Allow-Origin. W FastAPI dodajesz CORSMiddleware z listą dozwolonych źródeł. W teście żądanie z https://example.com dostało odpowiedni nagłówek, a z https://evil.com nagłówka nie dostało. Nie zezwalaj na wszystkie źródła (*) razem z danymi uwierzytelniającymi.

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com"],
    allow_methods=["*"],
    allow_headers=["*"],
)

Struktura i praktyka

Do czego służy APIRouter?

Pozwala podzielić aplikację na moduły: każdy ma własny router z prefiksem i tagami, a główny plik tylko je dołącza przez app.include_router. W teście router z prefiksem /api/v1 odpowiedział na /api/v1/ping.

from fastapi import APIRouter

router = APIRouter(prefix="/api/v1", tags=["v1"])

@router.get("/ping")
def ping():
    return {"pong": True}

app.include_router(router)

Co to są BackgroundTasks i kiedy ich użyć?

To zadania, które wykonują się po wysłaniu odpowiedzi, np. wysłanie maila potwierdzającego. W teście endpoint zwrócił odpowiedź, a zadanie dopisało wpis po handlerze (kolejność: handler, potem po odpowiedzi). To rozwiązanie na krótkie, mało krytyczne zadania. Do ciężkich i długich prac potrzebna jest kolejka zadań, bo BackgroundTasks działają w tym samym procesie.

Jak obsłużyć start i zamknięcie aplikacji (lifespan)?

Przez funkcję lifespan z yield: kod przed yield wykonuje się przy starcie (np. nawiązanie połączeń), a po nim przy zamknięciu. W teście zdarzenia to kolejno start i stop. To nowocześniejsza alternatywa dla starszych dekoratorów on_event.

Jak globalnie obsłużyć wyjątki?

Przez @app.exception_handler: rejestrujesz funkcję dla danego typu wyjątku, a FastAPI użyje jej zamiast domyślnej odpowiedzi 500. Dzięki temu własne błędy domenowe zamieniasz na spójne odpowiedzi JSON w jednym miejscu, a nie w każdym endpoincie. W teście wyjątek AppError obsłużony przez własny handler dał kod 418 i ciało {"error": "coś poszło nie tak"}.

from fastapi import Request
from fastapi.responses import JSONResponse

class AppError(Exception):
    def __init__(self, msg: str):
        self.msg = msg

@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError):
    return JSONResponse(status_code=418, content={"error": exc.msg})

Jak działa aplikacja z kilkoma workerami i czy współdzielą stan?

Każdy worker to osobny proces z własną pamięcią. W teście aplikacja uruchomiona przez uvicorn --workers 2 z globalnym licznikiem odpowiadała z dwóch procesów (różne pid), a każdy miał własny, niezależny licznik. Dlatego stanu, który ma być wspólny (sesje, liczniki, cache), nie trzymasz w zmiennej globalnej, tylko w zewnętrznym magazynie, np. w bazie albo Redisie. Uvicorn potrafi sam uruchomić kilka procesów, a częsta alternatywa to Gunicorn jako menedżer procesów z workerami Uvicorna. Różnice między tymi konfiguracjami opisujemy z dokumentacji, nie testowaliśmy ich.

Jak przyjąć plik w FastAPI?

Przez UploadFile i File. Walidację rozmiaru i typu oraz bezpieczny zapis pokazujemy w artykule o uploadzie plików w FastAPI.

Jak testuje się aplikację FastAPI?

Przez TestClient, który wywołuje aplikację bez uruchamiania serwera, zwykle razem z pytest. W połączeniu z dependency_overrides pozwala zastąpić bazę lub zewnętrzne API atrapą. Przykłady w tym artykule były sprawdzone właśnie w ten sposób.

Zadania z kodem i mini-test

Jakie zadania z FastAPI pojawiają się na rozmowach?

Najczęściej to krótkie zadania typu: „napisz endpoint, który zwraca listę z paginacją”, „dodaj walidację do tego modelu”, „zabezpiecz trasę tokenem” albo „znajdź błąd w tym kodzie”. Przy takich zadaniach liczy się nie szybkość pisania, tylko to, czy rozumiesz, dlaczego wybierasz dane rozwiązanie. Mów na głos, jak FastAPI potraktuje Twój kod.

Co jest nie tak w tym kodzie? Mini-test na rozgrzewkę

import time
import requests
from fastapi import FastAPI

app = FastAPI()

@app.post("/register")
async def register(user: dict):
    time.sleep(1)
    requests.post("https://example.com/hook", json=user)
    return user

Są tu co najmniej trzy problemy:

  1. Blokowanie pętli zdarzeń. time.sleep i requests.post to wywołania blokujące wewnątrz async def. Przy dwóch równoległych żądaniach takie endpointy wykonują się po kolei (w teście 1,0 s zamiast 0,5 s). Endpoint powinien być albo zwykłym def, albo używać await asyncio.sleep i asynchronicznego klienta (np. httpx.AsyncClient).
  2. Brak modelu Pydantic. Parametr user: dict przyjmuje cokolwiek i nie waliduje niczego. Powinien być modelem z typami i ograniczeniami.
  3. Zwracanie całego wejścia. Odpowiedź odsyła dokładnie to, co przyszło, w tym np. hasło. Potrzebny jest osobny model wyjściowy w response_model.

Jak przygotować się do rozmowy z FastAPI?

  1. Zbuduj małe API od zera. Kilka tras, model Pydantic, zależność z yield, uwierzytelnianie tokenem i testy z TestClient. Zacznij od pierwszego API w FastAPI.
  2. Przetestuj async na własne oczy. Zmierz czas dwóch równoległych żądań dla async def z blokującym sleep i dla zwykłego def. Rozumienie tej różnicy odróżnia kandydatów.
  3. Obejrzyj /docs. Sprawdź, jak zmiana typu lub dodanie response_model zmienia dokumentację. To najlepszy trening związku między kodem a schematem OpenAPI.
  4. Przećwicz odpowiedzi na głos. Dla każdego pytania z tego artykułu sformułuj w 30 sekund definicję i przykład.

Na czym juniorzy najczęściej tracą punkty?

Nie na trudnych tematach, tylko na trzech drobiazgach, które wychodzą dopiero przy pytaniach uzupełniających:

  1. async def wszędzie. Odpowiedź „async jest szybszy” kończy temat. Wyjaśnij, że blokujące wywołanie w async def zatrzymuje pętlę zdarzeń, i podaj, kiedy wybrać zwykły def.
  2. Brak response_model. Zwracanie obiektów z bazy wprost grozi wyciekiem pól, takich jak hasło. Pokaż, że rozdzielasz modele wejścia i wyjścia.
  3. Mylenie walidacji z autoryzacją. Pydantic sprawdza kształt danych, a nie to, czy użytkownik ma prawo do zasobu. To dwie osobne warstwy.

Jak brzmi dobra odpowiedź, a jak słaba?

Weźmy pytanie o async def i def:

OdpowiedźDlaczego
Słaba„async def jest szybszy, bo jest asynchroniczny.”Brak mechanizmu. Pytanie uzupełniające („co się stanie, jeśli w środku jest time.sleep?”) od razu to pokaże.
Dobra„async def działa w pętli zdarzeń, więc blokujące wywołanie w środku zatrzymuje wszystkie żądania. Zwykły def FastAPI uruchamia w wątku z puli. Używam async def tylko wtedy, gdy w środku jest await.”Jest różnica w zachowaniu, konsekwencja i reguła wyboru.

Ten sam schemat działa przy każdym pytaniu z tego artykułu: definicja, mechanizm, przykład użycia.

Co zrobić, gdy nie znasz odpowiedzi?

Nie zgaduj i nie milcz. Powiedz, co wiesz na pewno, a potem rozumuj na głos, np.: „Nie pamiętam nazwy tego narzędzia do zadań w tle, ale wiem, że szukam sposobu na wykonanie pracy po odpowiedzi, więc sprawdziłbym to w dokumentacji.” Taka odpowiedź pokazuje, że potrafisz sobie poradzić w pracy, a umiejętność znalezienia odpowiedzi jest w codziennej pracy ważniejsza niż pamięć do szczegółów.

Jeśli dopiero planujesz ścieżkę nauki, FastAPI opanujesz najszybciej, budując prawdziwe API razem z bazą danych i testami. W naszym programie robisz to na ścieżce nauki Pythona z mentorem. Ogólny przebieg całego procesu rekrutacji opisujemy w poradniku jak przygotować się do rekrutacji na juniora.

Konsultacja i warsztaty devmentor.pl

Masz pytania?

Partnerzy

Firmy, z którymi współpracujemy

To partnerzy odpowiedzialni za staże po naszych kursach. Organizują je dla najlepszych absolwentów.

Zobacz wszystkich partnerów →
  • VR Medical
  • Sophona
  • Robot Force