Last modified: September 03, 2023
This article is written in: 🇵🇱
FastAPI to nowoczesne, wydajne i łatwe w użyciu narzędzie do tworzenia API w Pythonie. Za jego popularność odpowiada prostota tworzenia aplikacji, wbudowana walidacja danych oraz automatyczne generowanie dokumentacji.
Aby zacząć pracę z FastAPI, musisz najpierw zainstalować odpowiednie pakiety. Oprócz samego FastAPI warto zainstalować serwer ASGI, np. uvicorn, który pozwoli na uruchamianie aplikacji:
pip install fastapi uvicornTworzenie API (Application Programming Interface) pozwala na komunikację między różnymi aplikacjami. W tym przykładzie wykorzystamy FastAPI, nowoczesny, szybki framework webowy dla Pythona, który ułatwia tworzenie API.
Rozpocznij od utworzenia instancji FastAPI i zdefiniowania kilku ścieżek (endpointów), które będą obsługiwać różne żądania HTTP.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
    return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}Wyjaśnienie:
app = FastAPI() – tworzy instancję aplikacji FastAPI.@app.get("/") – dekorator definiujący ścieżkę dla żądania GET na endpoint /.read_root() – funkcja obsługująca żądanie GET na /, zwracająca prosty słownik.@app.get("/items/{item_id}") – dynamiczny endpoint, który przyjmuje parametr item_id.read_item(item_id: int, q: str = None) – funkcja obsługująca żądanie, gdzie item_id jest wymaganym parametrem, a q jest opcjonalnym zapytaniem.Aby uruchomić powyższe API, zapisz kod do pliku, np. main.py. Następnie masz kilka opcji uruchomienia serwera:
uvicorn main:app --reloadWyjaśnienie:
main:app – odnosi się do obiektu app w pliku main.py.--reload – automatycznie restartuje serwer przy zmianach w kodzie (przydatne w trakcie developmentu).Możesz również uruchomić Uvicorn bezpośrednio z kodu Pythona:
import uvicorn
if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)Wyjaśnienie:
uvicorn i wywołujesz uvicorn.run() z odpowiednimi parametrami.Uvicorn to szybki, asynchroniczny serwer ASGI napisany w Pythonie, używany do uruchamiania aplikacji webowych opartych na ASGI, takich jak FastAPI. Umożliwia obsługę wielu żądań jednocześnie dzięki asynchronicznej naturze.
Alternatywy dla Uvicorn:
uvicorn.workers.UvicornWorker) – popularny serwer WSGI, który można skonfigurować do obsługi ASGI.Jeśli planujesz uruchomić serwer z linii komend (cmd), warto zwrócić uwagę na kilka różnic między systemami operacyjnymi:
Instalacja pakietów:
pip bez dodatkowych modyfikacji.sudo dla instalacji globalnych lub korzystanie z wirtualnych środowisk.Ścieżki plików:
\), podczas gdy Linux używa slash (/).os.path lub pathlib.Uruchamianie skryptów:
cmd lub PowerShell.Zarządzanie procesami:
systemd do zarządzania usługami.Co sprawdzić przed uruchomieniem:
venv lub virtualenv) dla izolacji zależności.Po uruchomieniu serwera możesz przejść do przeglądarki i otworzyć adres http://localhost:8000. Zobaczysz odpowiedź z powitaniem:
{
  "Hello": "World"
}Dodatkowo, FastAPI automatycznie generuje interaktywną dokumentację API dostępną pod adresami:
http://localhost:8000/docshttp://localhost:8000/redocMożesz tam przetestować swoje endpointy bezpośrednio z przeglądarki.
Jednym z popularnych narzędzi do definiowania i walidacji modelu danych jest Pydantic. Dzięki niemu możemy w sposób deklaratywny określić strukturę naszych danych oraz oczekiwane typy wartości.
Poniżej mamy przykład modelu Item:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
items = {}  # prosty "słownik" do przechowywania danych
class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = NoneModel ten przedstawia przedmiot, który posiada następujące atrybuty:
name: Nazwa przedmiotu. Jest to pole wymagane i powinno być typu str.description: Opis przedmiotu. Jest to pole opcjonalne i również powinno być typu str.price: Cena przedmiotu. Jest to pole wymagane i powinno być typu float lub int.tax: Podatek dla danego przedmiotu. Jest to pole opcjonalne i, jeśli podane, powinno być typu float lub int./items/items) z unikalnym ID.
@app.post("/items/")
def create_item(item: Item):
    item_id = len(items) + 1
    items[item_id] = item
    return {"id": item_id, **item.dict()}/items/{item_id}item_id zwraca dane przedmiotu.
@app.get("/items/{item_id}")
def read_item(item_id: int):
    item = items.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item/items/{item_id}item_id usuwa przedmiot.
@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    del items[item_id]
    return {"status": "success"}Poniżej znajduje się pełny, wykonywalny przykład API do zarządzania przedmiotami, wykorzystujący biblioteki FastAPI oraz Pydantic do definicji modelu danych. Zawiera definicję modelu oraz funkcje do tworzenia, odczytywania i usuwania przedmiotów przechowywanych w prostym słowniku.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
app = FastAPI()
# Przechowywanie danych w słowniku
items = {}
# Definicja modelu danych przy użyciu Pydantic
class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None
# Endpoint do tworzenia przedmiotu (POST)
@app.post("/items/")
def create_item(item: Item):
    item_id = len(items) + 1
    items[item_id] = item
    return {"id": item_id, **item.dict()}
# Endpoint do odczytywania przedmiotu (GET)
@app.get("/items/{item_id}")
def read_item(item_id: int):
    item = items.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item
# Endpoint do usuwania przedmiotu (DELETE)
@app.delete("/items/{item_id}")
def delete_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    del items[item_id]
    return {"status": "success"}
# Uruchomienie serwera
if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)Funkcja uvicorn.run(app, host="127.0.0.1", port=8000) określa, że aplikacja ma być dostępna pod adresem localhost (127.0.0.1) na porcie 8000.
Aby uruchomić ten serwer, wystarczy, że uruchomisz ten plik bezpośrednio z Pythona, używając polecenia:
python nazwa_pliku.pyGdzie nazwa_pliku.py to nazwa pliku, w którym umieściłeś powyższy kod.
Swagger UI jest automatycznie generowanym interfejsem użytkownika dla dokumentacji API, dostępnym w FastAPI dzięki integracji z OpenAPI. Dostarcza on przeglądarkę dokumentacji, która pozwala na interaktywne eksplorowanie API, wykonywanie żądań i przeglądanie odpowiedzi bezpośrednio z przeglądarki internetowej.
Aby komunikować się z API w Pythonie, potrzebujemy biblioteki requests, która upraszcza proces wysyłania żądań HTTP i obsługi odpowiedzi. Bibliotekę tę możemy zainstalować za pomocą poniższej komendy:
pip install requestsBiblioteka requests jest niezwykle popularna ze względu na swoją prostotę i elastyczność. Umożliwia łatwe wysyłanie różnych typów żądań HTTP (GET, POST, PUT, DELETE itp.), zarządzanie nagłówkami, obsługę parametrów zapytań oraz przetwarzanie odpowiedzi w formatach takich jak JSON.
Poniżej znajdują się przykłady, jak używać tej biblioteki do tworzenia, pobierania i usuwania przedmiotów za pomocą API.
Metoda requests.post pozwala na wysłanie danych w formacie JSON do określonego endpointu API, co zazwyczaj służy do tworzenia nowych zasobów na serwerze. W tym przypadku tworzymy nowy przedmiot w systemie.
Przykład kodu:
import requests
# URL API
url = "http://127.0.0.1:8000/items/"
# Dane przedmiotu
item_data = {
    "name": "example",
    "description": "This is an example item",
    "price": 10.0,
    "tax": 2.0
}
# Wysłanie żądania POST
response = requests.post(url, json=item_data)
# Wyświetlenie odpowiedzi
print(response.status_code)  # Sprawdzenie statusu odpowiedzi
print(response.json())       # Wyświetlenie odpowiedzi w formacie JSONSzczegółowe wyjaśnienie:
requests, która umożliwia nam interakcję z API.url wskazuje na endpoint API, do którego wysyłamy żądanie. W tym przypadku jest to lokalny serwer działający na porcie 8000.item_data to słownik zawierający dane nowego przedmiotu, które chcemy utworzyć. Dane te zostaną automatycznie przekształcone do formatu JSON przez parametr json w metodzie post.requests.post wysyła żądanie POST do określonego URL z przekazanymi danymi.response.status_code pozwala na sprawdzenie, czy żądanie zakończyło się sukcesem (np. status 201 Created), a response.json() zwraca odpowiedź serwera w formacie JSON, co może zawierać szczegóły utworzonego przedmiotu.Aby pobrać dane konkretnego przedmiotu, używamy metody requests.get z odpowiednim item_id. Żądanie GET służy do odczytywania danych z serwera bez ich modyfikacji.
Przykład kodu:
import requests
# URL API z dodanym item_id
url = "http://127.0.0.1:8000/items/1"
# Wysłanie żądania GET
response = requests.get(url)
# Wyświetlenie odpowiedzi
if response.status_code == 200:
    print(response.json())  # Wyświetlenie danych przedmiotu
else:
    print("Item not found")  # Wyświetlenie informacji, jeśli przedmiot nie został znalezionySzczegółowe wyjaśnienie:
requests.url zawiera endpoint API wraz z identyfikatorem przedmiotu, który chcemy pobrać (w tym przypadku 1).requests.get wysyła żądanie GET do określonego URL.200 OK, oznacza to, że przedmiot został znaleziony i możemy wyświetlić jego dane. W przeciwnym razie informujemy użytkownika, że przedmiot nie został znaleziony.Dodatkowe informacje:
I. Parametry zapytania:
Jeśli API obsługuje filtrowanie lub paginację, możemy przekazać dodatkowe parametry za pomocą argumentu params. Przykład:
params = {'category': 'electronics', 'limit': 10}
response = requests.get(url, params=params)II. Nagłówki:
Możemy również dodać nagłówki do żądania, takie jak autoryzacja czy specyficzne formaty odpowiedzi.
headers = {'Authorization': 'Bearer YOUR_TOKEN'}
response = requests.get(url, headers=headers)Aby usunąć przedmiot na podstawie item_id, używamy metody requests.delete. Żądanie DELETE służy do usuwania zasobów z serwera.
Przykład kodu:
import requests
# URL API z dodanym item_id
url = "http://127.0.0.1:8000/items/1"
# Wysłanie żądania DELETE
response = requests.delete(url)
# Wyświetlenie odpowiedzi
if response.status_code == 200:
    print(response.json())  # Wyświetlenie statusu usunięcia
else:
    print("Item not found")  # Wyświetlenie informacji, jeśli przedmiot nie został znalezionySzczegółowe wyjaśnienie:
requests.url zawiera endpoint API wraz z identyfikatorem przedmiotu do usunięcia.requests.delete wysyła żądanie DELETE do określonego URL.200 OK, oznacza to, że przedmiot został pomyślnie usunięty, a serwer może zwrócić potwierdzenie w formacie JSON. Jeśli przedmiot nie został znaleziony, informujemy o tym użytkownika.Dodatkowe informacje:
I. Obsługa błędów:
Biblioteka requests pozwala na łatwą obsługę błędów poprzez sprawdzanie statusów odpowiedzi. Możemy również użyć wyjątków do zarządzania nieoczekiwanymi sytuacjami.
try:
   response = requests.get(url)
   response.raise_for_status()  # Podniesie wyjątek dla statusów błędu
   data = response.json()
except requests.exceptions.HTTPError as err:
   print(f"HTTP error occurred: {err}")
except requests.exceptions.RequestException as err:
   print(f"Other error occurred: {err}")II. Autoryzacja:
Jeśli API wymaga uwierzytelnienia, możemy dodać odpowiednie nagłówki lub użyć mechanizmów takich jak tokeny Bearer.
headers = {
   'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
   'Content-Type': 'application/json'
}
response = requests.get(url, headers=headers)III. Wysyłanie danych w innych formatach:
Chociaż JSON jest najczęściej używanym formatem, requests umożliwia wysyłanie danych w innych formatach, takich jak formy HTML czy pliki.
# Wysyłanie danych jako formularz
form_data = {'field1': 'value1', 'field2': 'value2'}
response = requests.post(url, data=form_data)
# Wysyłanie pliku
files = {'file': open('report.xls', 'rb')}
response = requests.post(url, files=files)IV. Parametryzacja URL:
Dla bardziej dynamicznych URL-ów możemy użyć funkcji formatowania stringów lub modułu urllib.parse.
item_id = 1
url = f"http://127.0.0.1:8000/items/{item_id}"
# lub
from urllib.parse import urljoin
base_url = "http://127.0.0.1:8000/items/"
url = urljoin(base_url, str(item_id))V. Timeouts:
Aby zapobiec zawieszaniu się programu w przypadku braku odpowiedzi z serwera, warto ustawić limit czasu dla żądań.
response = requests.get(url, timeout=5)  # Timeout po 5 sekundachVI. Sesje:
Używanie obiektów sesji (requests.Session) pozwala na utrzymanie pewnych parametrów między żądaniami, takich jak nagłówki czy ciasteczka.
session = requests.Session()
session.headers.update({'Authorization': 'Bearer YOUR_ACCESS_TOKEN'})
response = session.get(url)
response = session.post(url, json=item_data)