Hej, hej... Programisto, to kolejny artykuł dla Ciebie! Druga część artykułu na temat wzorców projektowych. Poznaj Adapter oraz Memento.
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.
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:
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
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.
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ł:
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ć).
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