FastAPI – czyli jak napisać proste REST API w Pythonie? – część 1

Autor Autor:
Zespół Innokrea
Data publikacji: 2024-07-18
Kategorie: Programowanie

Witamy ponownie! Dzisiaj jako Innokrea pokażemy Wam jak możecie zrobić własny projekt w Pythonie wykorzystując do tego FastAPI oraz MongoDB. Pokażemy Wam także jak dodać testy i sprawić, żeby wszystko działało poprawnie w Dockerze. Jeśli jesteście ciekawi, to zapraszamy do lektury!

 

REST API

Rest API to jedna z najpopularniejszych architektur typu klient-serwer używanych obecnie w aplikacjach, definiująca to, w jaki sposób zasoby mogą być dostępne w sieci i przesyłane poprzez Internet. Najważniejszymi cechami są: bezstanowość, interfejs dostępny zwykle poprzez protokół HTTP oraz identyfikacja zasobów poprzez URI. Do implementacji REST API używa się frameworków dostępnych w wielu językach takich jak Java (np. Spring), JavaScript (np. express.js) czy Python (np. Django czy FastAPI).

Jeśli chcecie poczytać o tym jakie są najlepsze praktyki w projektowaniu REST API, to zachęcamy do przeczytania poniższego artykułu z bloga stackoverflow.

https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/

 

FastAPI – jak zacząć? – środowisko deweloperskie

FastAPI jest jednym z najpopularniejszych frameworków napisanym w języku Python. Nie jest to najszybsze dostępne na rynku rozwiązanie, ale jedno z najszybszych w Pythonie. Dzięki prostocie FastAPI zwiększa prędkość wytwarzania oprogramowania oraz zmniejsza możliwość popełnienia błędu przez dewelopera.

Zacznijmy od stworzenia folderu innokrea, a w nim dodatkowego folderu rest, w którym umieścimy nasz projekt.

 

FastAPI - Utworzenie projektu

Rysunek 1 – Utworzenie projektu

 

Zaczniemy od utworzenia dwóch folderów app oraz tests oraz plików Dockerfile i requirements.dev. Całe rozwiązanie będziemy od razu, nawet podczas rozwoju aplikacji, uruchamiać w środowisku Docker, aby instalacja była prosta dla każdego.

Struktura folderów powinna wyglądać następująco:

 

FastAPI - Struktura folderów naszego projektu

Rysunek 2 – Struktura folderów naszego projektu

 

Zawartość plików Dockerfile powinna wyglądać następująco:

FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY requirements.dev /app/
RUN apt-get update && \
    pip install --no-cache-dir -r requirements.dev
COPY ./app /app/
WORKDIR /
EXPOSE 8000
CMD ["uvicorn", "app.app:app", "--host", "0.0.0.0", "--port", "8000","--reload"]

 

A pliku requirements.dev tak:

annotated-types==0.6.0
pydantic==2.4.2
uvicorn==0.23.2
fastapi==0.103.1
pymongo==4.6.2
pyjwt==2.8.0
passlib==1.7.4
httpx==0.27.0
asyncio==3.4.3
#Testing requirements
pytest==7.1.3
setuptools==69.2.0
mongomock==4.1.2
freezegun==1.4.0
mock==5.1.0

 

Zawartość pliku app.py:

from fastapi import FastAPI

app = FastAPI()
@app.get("/test")
async def test_endpoint():
 return {"message": "This is a test endpoint!"}

 

Teraz, aby uruchomić projekt, stworzymy jeszcze plik docker-compose-dev.yml, który będzie odpowiadał za zbudowanie naszego obrazu. Utworzymy także bind mount, aby dynamicznie przeładowywać kod, zamiast przebudowywać obraz po każdej edycje.

version: "3.9"
services:
 rest-service:
   build:
     context: .
     # Dev version of Dockerfile
     dockerfile: Dockerfile
   container_name: rest-service
   restart: always
   ports:
     - "8000:8000"
   volumes:
     - ./app:/app
   networks:
     - network
networks:
 network:

 

Po wykonaniu poniższej komendy powinniśmy zobaczyć w przeglądarce pod adresem localhost:8000/test odpowiedź od FastAPI.

docker-compose -f docker-compose-dev.yml up --build

 

FastAPI - działająca aplikacja

Rysunek 3 – działająca aplikacja

 

Dodatkowo, przy edycji kodu, nasza aplikacja powinna się automatycznie przeładować.

 

Nasza aplikacja – architektura i baza danych

Mamy gotowe podstawowe środowisko deweloperskie, ale właściwie jaką aplikację będziemy robić? Spróbujemy uruchomić bazę danych i zrobić prostą aplikację pozwalającą na rejestrację użytkowników i ich logowanie. Na początek zaproponujemy warstwową architekturę aplikacji tak, aby rozdzielić odpowiedzialność aplikacji na kilka sekcji związanych z obsługą żądania, logiki biznesowej czy danych.

 

FastAPI - Architektura warstwowa naszej aplikacji

Rysunek 4 – Architektura warstwowa naszej aplikacji, źródło [1]

 

Utwórzmy strukturę folderów, która pomoże nam w zbudowaniu tej architektury warstwowej. Będziemy mieli następujący podział:

  • controllers – definicje endpointów
  • database – połączenie do bazy danych
  • dependencies – instancje obiektów potrzebnych do działania API
  • exceptions – definicje i obsługa wyjątków
  • models – klasy modelowe korespondujące do schematu bazy
  • repositories – obsługa warstwy dostępu do danych
  • schemas – klasy modelowe dla żądań i odpowiedzi
  • services – logika biznesowa

 

FastAPI - Podział folderów w naszej aplikacji

Rysunek 5 – Podział folderów w naszej aplikacji

 

Przejdźmy teraz do stworzenia bazy danych, której będziemy używać do zapisania użytkowników. W tym celu znowu użyjemy Dockera i bazy danych mongodb oraz biblioteki pymongo.

Dodajmy więc następujący skrypt inicjalizujący bazę danych do folderu ‘rest’ o nazwie db-init.js.

db = db.getSiblingDB("db");
db.createCollection("users");
db.users.insertMany([{
 email: "aaa@aaa.com",
 role: "user",
 password_hash: "9c520caf74cff9b9a891be3694b20b3586ceb17f2891ceb1d098709c1e0969a3",
},{
 email: "bbb@bbb.com",
 role: "user",
 password_hash: "77cd27bc3de668c18ed6be5f5c2909ffdacdf67705c30d132003ad5a89085deb",
},]);

 

Hasło dla każdego z użytkowników jest takie samo jak login. Następnie wklejmy zmodyfikowany plik docker-compose-dev.yml który dodaje bazę danych na porcie 27017 oraz panel administracyjny do tej bazy na porcie 8080. Dodaliśmy także niezbędne dla projektu zmienne środowiskowe.

version: "3.9"
services:
 rest-service:
   build:
     context: .
     # Dev version of Dockerfile
     dockerfile: Dockerfile
   container_name: rest-service
   restart: always
   environment:
     - WATCHFILES_FORCE_POLLING=true
     - DB_HOSTNAME=db
     - DB_USERNAME=root
     - DB_PASSWORD=root
     - DB_PORT=27017
     - DB_NAME=db

     # JWT CONF
     - JWT_TOKEN_ALG=HS256
     - JWT_REFRESH_TOKEN_SECRET_KEY=refreshsecret
     - JWT_ACCESS_TOKEN_SECRET_KEY=accesssecret
     - JWT_ACCESS_TOKEN_EXPIRE_MINUTES=10080
     - JWT_REFRESH_TOKEN_EXPIRE_MINUTES=30
   ports:
     - "8000:8000"
   volumes:
     - ./app:/app
   networks:
     - network
 db:
   image: bitnami/mongodb:7.0.7-debian-12-r0
   container_name: db
   restart: always
   environment:
     - MONGODB_REPLICA_SET_MODE=primary
     - MONGODB_REPLICA_SET_KEY=123456
     - ALLOW_EMPTY_PASSWORD=yes
     - MONGODB_ROOT_USER=root
     - MONGODB_ROOT_PASSWORD=root
   healthcheck:
     test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/db --quiet
     interval: 10s
     timeout: 10s
     retries: 3
     start_period: 20s
   volumes:
     - ./db-init.js:/docker-entrypoint-initdb.d/initialize.js
   networks:
     - network

 adminpanel-db:
   image: mongo-express:1.0.2-20-alpine3.19
   container_name: adminpanel-db
   restart: always
   depends_on:
     - db
   ports:
     - "8080:8081"
   environment:
     - ME_CONFIG_MONGODB_SERVER=db
     - ME_CONFIG_MONGODB_URL=mongodb://root:root@db:27017/
     - ME_CONFIG_MONGODB_AUTH_USERNAME=root
     - ME_CONFIG_MONGODB_AUTH_PASSWORD=root
     - ME_CONFIG_MONGODB_PORT=27017
   # to login to panel use: admin/pass
   networks:
     - network

networks:
 network:

 

Teraz spróbujmy ponownie uruchomić naszą aplikację z użyciem docker-compose-dev.yml i komend:

docker-compose -f docker-compose-dev.yml down
docker-compose -f docker-compose-dev.yml up --build

 

Powinniśmy być w stanie zobaczyć zainicjalizowaną kolekcję na localhost:8080 (admin/pass aby się zalogować).

 

FastAPI - Widok zainicjalizowanej kolekcji w panelu administratora

Rysunek 6 – Widok zainicjalizowanej kolekcji w panelu administratora

 

Podsumowanie

Dzisiaj udało nam się stworzyć podstawową konfigurację naszego środowiska złożonego z FastAPI, bazy danych i panelu administratora. Za tydzień spróbujemy rozwinąć naszą aplikację i wypełnić stworzone foldery plikami z kodem. Do zobaczenia!

 

Kod do pobrania na naszym gitlabie!

 

 

Źródła:

[1] https://dev.to/blindkai/backend-layered-architecture-514h

Zobacz więcej na naszym blogu:

Zarządzanie tożsamością i dostępem użytkownika, czyli o co chodzi z IDP?

Zarządzanie tożsamością i dostępem użytkownika, czyli o co chodzi z IDP?

Czym jest tożsamość użytkownika? Z czego wynika potrzeba zarządzania dostępem w firmie? Jak działa tzw. IDP? Odpowiedź na te pytania znajdziesz w artykule.

Bezpieczeństwo

Wzorce projektowe – część 2

Wzorce projektowe – część 2

Hej, hej... Programisto, to kolejny artykuł dla Ciebie! Druga część artykułu na temat wzorców projektowych. Poznaj Adapter oraz Memento.

Programowanie

Wzorce projektowe – część 1

Wzorce projektowe – część 1

Programisto, to artykuł dla Ciebie! Łap garść przydatnych informacji na temat wzorców projektowych.

Programowanie