- Backend – odpowiedzialny za logikę biznesową, API i dane (np. Django REST Framework, FastAPI),
- Frontend – zajmujący się interfejsem użytkownika (React, Next.js, Vue).
To podejście pozwala tworzyć bardziej skalowalne i elastyczne aplikacje, ale wymaga prawidłowej integracji między warstwami. W tym artykule dowiesz się, jak połączyć Pythonowy backend (Django lub FastAPI) z Reactem, zachowując bezpieczeństwo, wydajność i wygodę pracy.
Architektura aplikacji frontend-backend
Typowa architektura aplikacji typu SPA (Single Page Application) wygląda tak:
Bash1[React/Next.js frontend] → [Python API: Django / FastAPI] → [Database]
Frontend komunikuje się z backendem poprzez REST API lub GraphQL. Backend zwraca dane w formacie JSON, które frontend przetwarza i renderuje w przeglądarce.
Aby to działało, musimy:
- Włączyć CORS, aby frontend mógł komunikować się z backendem z innego adresu,
- Zaimplementować autoryzację (JWT, sesje),
- Zaprojektować czytelne API endpoints,
- Skonfigurować środowisko deweloperskie i produkcyjne.
Konfiguracja CORS
CORS (Cross-Origin Resource Sharing) pozwala przeglądarce komunikować się z API działającym pod inną domeną (np. frontend: localhost:3000, backend: localhost:8000).
Django + Django REST Framework
Zainstaluj bibliotekę:
Bash1pip install django-cors-headers
W pliku settings.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47INSTALLED_APPS = [ ... "corsheaders", "rest_framework", ] MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware",# Musi być na początku "django.middleware.common.CommonMiddleware", ... ] # Dozwolone źródła (origins) CORS_ALLOWED_ORIGINS = [ "http://localhost:3000", "http://localhost:5173",# Vite "https://twojfrontend.pl" ] # Alternatywnie - dozwól wszystko w trybie deweloperskim # CORS_ALLOW_ALL_ORIGINS = True# Tylko w development! # Dozwolone metody HTTP CORS_ALLOW_METHODS = [ "DELETE", "GET", "OPTIONS", "PATCH", "POST", "PUT", ] # Dozwolone nagłówki CORS_ALLOW_HEADERS = [ "accept", "accept-encoding", "authorization", "content-type", "dnt", "origin", "user-agent", "x-csrftoken", "x-requested-with", ] # Zezwól na przesyłanie cookies CORS_ALLOW_CREDENTIALS = True
Konfiguracja CORS w FastAPI
FastAPI ma wbudowane wsparcie dla CORS:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=[ "http://localhost:3000", "http://localhost:5173", "https://twojfrontend.pl" ], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
Dzięki temu frontend może bez problemu wykonywać zapytania AJAX/FETCH do backendu.
Testowanie CORS
Możesz sprawdzić, czy CORS działa poprawnie, używając cURL:
Bash1 2 3 4 5curl -H "Origin: http://localhost:3000" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Content-Type" \ -X OPTIONS \ http://localhost:8000/api/posts/
Odpowiedź powinna zawierać nagłówki Access-Control-Allow-Origin, Access-Control-Allow-Methods, itp.
Autoryzacja – JWT lub sesje
Integracja backendu i frontendu wymaga spójnego mechanizmu autoryzacji.
JWT (JSON Web Tokens)
Idealne w aplikacjach SPA lub mobilnych, gdzie frontend przechowuje tokeny w pamięci (np. localStorage lub cookie).
Django REST Framework
Zainstaluj:
Bash1pip install djangorestframework-simplejwt
W settings.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22INSTALLED_APPS = [ ... "rest_framework", "rest_framework_simplejwt", ] REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "rest_framework_simplejwt.authentication.JWTAuthentication", ), "DEFAULT_PERMISSION_CLASSES": ( "rest_framework.permissions.IsAuthenticated", ), } from datetime import timedelta SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), "REFRESH_TOKEN_LIFETIME": timedelta(days=7), "ROTATE_REFRESH_TOKENS": True, }
Endpoint logowania w urls.py:
Python1 2 3 4 5 6 7from django.urls import path from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView urlpatterns = [ path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), ]
Implementacja JWT w FastAPI
FastAPI obsługuje JWT natywnie:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import jwt, JWTError from datetime import datetime, timedelta SECRET_KEY = "your-secret-key" ALGORITHM = "HS256" oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=30) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception return {"username": username} @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): # Weryfikacja użytkownika user = authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException(status_code=401, detail="Invalid credentials") access_token = create_access_token(data={"sub": user.username}) return {"access_token": access_token, "token_type": "bearer"} @app.get("/users/me") async def read_users_me(current_user: dict = Depends(get_current_user)): return current_user
Użycie JWT w React
JavaScript1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23// Login const response = await fetch('http://localhost:8000/api/token/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: 'user', password: 'pass' }) }); const data = await response.json(); localStorage.setItem('access_token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); // Autoryzowane zapytania const token = localStorage.getItem('access_token'); const response = await fetch('http://localhost:8000/api/users/me', { headers: { 'Authorization': `Bearer ${token}` } });
Sesje
Dla aplikacji działających w przeglądarce klasycznie (SSR lub hybrydowo), można użyć sesji HTTP-only.
Django - Sesje z CSRF
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22# settings.py SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' CSRF_COOKIE_HTTPONLY = True # W widoku from django.contrib.auth import login from django.views.decorators.csrf import csrf_exempt from django.middleware.csrf import get_token @csrf_exempt def login_view(request): if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user: login(request, user) return JsonResponse({'status': 'ok'}) csrf_token = get_token(request) return JsonResponse({'csrf_token': csrf_token})
Implementacja sesji w FastAPI
Bash1pip install python-multipart
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27from fastapi import FastAPI, Request from starlette.middleware.sessions import SessionMiddleware from starlette.responses import JSONResponse app = FastAPI() app.add_middleware(SessionMiddleware, secret_key="your-secret-key") @app.post("/login") async def login(request: Request): form = await request.form() username = form.get("username") password = form.get("password") # Weryfikacja user = authenticate_user(username, password) if user: request.session["user_id"] = user.id return JSONResponse({"status": "ok"}) return JSONResponse({"status": "error"}, status_code=401) @app.get("/users/me") async def read_users_me(request: Request): user_id = request.session.get("user_id") if not user_id: return JSONResponse({"detail": "Not authenticated"}, status_code=401) return {"user_id": user_id}
API endpoints i struktura danych
Frontend potrzebuje czytelnych i przewidywalnych API endpoints.
Przykład dobrego REST API
GET/api/posts/ → lista postów
GET/api/posts/1/ → szczegóły posta
POST /api/posts/ → utworzenie posta
PUT/api/posts/1/ → edycja posta
PATCH/api/posts/1/ → częściowa edycja
DELETE /api/posts/1/ → usunięcie posta
Spójna struktura odpowiedzi
Zwracaj dane w spójnej strukturze JSON:
JSON1 2 3 4 5 6{ "id": 1, "title": "Integracja Django z React", "author": "Kacper Sieradziński", "created_at": "2025-05-30T12:00:00Z" }
Dla list:
JSON1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17{ "count": 100, "next": "http://api.example.com/api/posts/?page=2", "previous": null, "results": [ { "id": 1, "title": "Post 1", ... }, { "id": 2, "title": "Post 2", ... } ] }
Statusy HTTP
Zachowuj standardowe statusy HTTP:
200 OK– sukces (GET, PUT, PATCH)201 Created– nowy rekord utworzony (POST)204 No Content– sukces bez treści (DELETE)400 Bad Request– błędne dane401 Unauthorized– brak autoryzacji403 Forbidden– brak uprawnień404 Not Found– nie znaleziono zasobu422 Unprocessable Entity– błędy walidacji500 Internal Server Error– błąd serwera
Przykład endpointu w Django REST Framework
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14from rest_framework import viewsets, status from rest_framework.response import Response from rest_framework.decorators import action class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.all() serializer_class = PostSerializer @action(detail=True, methods=['post']) def publish(self, request, pk=None): post = self.get_object() post.published = True post.save() return Response({'status': 'published'})
Przykład REST endpointu w FastAPI
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39from fastapi import FastAPI, HTTPException, status from pydantic import BaseModel from typing import List from datetime import datetime app = FastAPI() class Post(BaseModel): id: int title: str author: str created_at: str class PostCreate(BaseModel): title: str author: str posts_db = [] @app.get("/api/posts/", response_model=List[Post]) def list_posts(skip: int = 0, limit: int = 10): return posts_db[skip:skip + limit] @app.get("/api/posts/{post_id}", response_model=Post) def get_post(post_id: int): post = next((p for p in posts_db if p["id"] == post_id), None) if not post: raise HTTPException(status_code=404, detail="Post not found") return post @app.post("/api/posts/", status_code=status.HTTP_201_CREATED, response_model=Post) def create_post(post: PostCreate): new_post = { "id": len(posts_db) + 1, **post.dict(), "created_at": datetime.now().isoformat() } posts_db.append(new_post) return new_post
Synchronizacja środowisk developerskich
Aby uniknąć konfliktów:
Uruchamianie backendu i frontendu równolegle
Terminal 1 - Backend:
Bash1 2 3 4cd backend python manage.py runserver# Django uvicorn main:app --reload # FastAPI
Terminal 2 - Frontend:
Bash1 2 3 4cd frontend npm start# React npm run dev# Next.js/Vite
Proxy w React
Użyj proxy w React, aby uprościć adresy żądań.
W pliku package.json:
JSON1 2 3 4{ "name": "my-app", "proxy": "http://127.0.0.1:8000" }
Dzięki temu w React możesz pisać:
JavaScript1fetch("/api/posts")
zamiast:
JavaScript1fetch("http://127.0.0.1:8000/api/posts")
Konfiguracja proxy w Vite
Jeśli używasz Vite, skonfiguruj proxy w vite.config.js:
JavaScript1 2 3 4 5 6 7 8 9 10export default { server: { proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, } } } }
Zmienne środowiskowe
Django:
Python1 2 3# settings.py FRONTEND_URL = os.environ.get('FRONTEND_URL', 'http://localhost:3000') CORS_ALLOWED_ORIGINS = [FRONTEND_URL]
FastAPI:
Python1 2 3 4 5 6 7 8 9 10# config.py from pydantic_settings import BaseSettings class Settings(BaseSettings): frontend_url: str = "http://localhost:3000" class Config: env_file = ".env" settings = Settings()
React (.env):
Bash1REACT_APP_API_URL=http://localhost:8000
Użycie w React:
JavaScript1 2const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000'; fetch(`${API_URL}/api/posts`);
Deploy – Django + React w produkcji
Masz dwa główne podejścia do wdrożenia:
1. Wspólny serwer (monolit)
Django serwuje statyczny build frontendu.
Konfiguracja Django
W settings.py:
Python1 2 3 4 5 6 7 8 9 10 11import os STATICFILES_DIRS = [ BASE_DIR / "frontend/build/static" ] STATIC_ROOT = BASE_DIR / "staticfiles" # Pliki media (uploady użytkowników) MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / "media"
W urls.py:
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22from django.contrib import admin from django.urls import path, re_path from django.views.static import serve from django.views.generic import TemplateView from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path("admin/", admin.site.urls), path("api/", include("api.urls")), # Serwuj statyczne pliki w produkcji re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), # Wszystkie inne ścieżki → React Router re_path(r'^.*$', TemplateView.as_view(template_name='index.html')), ] # W development if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Build frontendu
Bash1 2 3cd frontend npm run build cp -r build/* ../backend/frontend/
2. Oddzielne usługi (mikroserwisy)
Frontend (React) hostowany na CDN (np. Vercel, Netlify), backend (FastAPI/Django) na serwerze API (np. Render, Railway, AWS).
Konfiguracja dla oddzielnych serwisów
Backend (Django/FastAPI):
Python1 2 3 4 5# settings.py / config.py CORS_ALLOWED_ORIGINS = [ "https://twoj-frontend.vercel.app", "https://twoj-frontend.netlify.app" ]
Frontend:
JavaScript1 2 3 4// config.js const API_URL = process.env.REACT_APP_API_URL || 'https://api.twoj-backend.com'; export { API_URL };
Docker Compose (dla pełnej kontroli)
YAML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18version: '3.9' services: backend: build: ./backend ports: - "8000:8000" environment: - CORS_ALLOWED_ORIGINS=http://localhost:3000 frontend: build: ./frontend ports: - "3000:3000" environment: - REACT_APP_API_URL=http://localhost:8000 depends_on: - backend
Współpraca między zespołami frontend/backend
Dobre praktyki komunikacji:
Wspólna dokumentacja API
Swagger/OpenAPI (FastAPI automatycznie):
- Dostępna pod
/docs(Swagger UI) - Dostępna pod
/redoc(ReDoc)
Django REST Framework:
Bash1pip install drf-yasg
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14from drf_yasg.views import get_schema_view from drf_yasg import openapi schema_view = get_schema_view( openapi.Info( title="My API", default_version='v1', ), public=True, ) urlpatterns = [ path('swagger/', schema_view.with_ui('swagger', cache_timeout=0)), ]
Mock API
Frontend może testować interfejs zanim backend będzie gotowy. Użyj narzędzi takich jak:
- MSW (Mock Service Worker) – mockowanie API w przeglądarce
- JSON Server – szybkie REST API z JSON
- Postman Mock Servers
Wersjonowanie API
Zawsze wersjonuj API:
/api/v1/posts/
/api/v2/posts/
Django:
Python1 2 3 4urlpatterns = [ path('api/v1/', include('api.v1.urls')), path('api/v2/', include('api.v2.urls')), ]
FastAPI:
Python1 2 3 4 5 6 7from fastapi import APIRouter v1 = APIRouter(prefix="/api/v1", tags=["v1"]) v2 = APIRouter(prefix="/api/v2", tags=["v2"]) app.include_router(v1) app.include_router(v2)
Stałe formaty błędów
Zawsze zwracaj błędy w spójnym formacie:
JSON1 2 3 4{ "detail": "Invalid credentials", "code": "AUTH_ERROR" }
lub dla błędów walidacji:
JSON1 2 3 4 5 6{ "errors": { "email": ["This field is required."], "password": ["Password must be at least 8 characters."] } }
Przykład pełnego przepływu logowania
Implementacja logowania w FastAPI
Python1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42from fastapi import FastAPI, Depends, HTTPException from fastapi.security import OAuth2PasswordRequestForm from jose import jwt from datetime import datetime, timedelta app = FastAPI() SECRET_KEY = "your-secret-key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) @app.post("/api/token/") async def login(form_data: OAuth2PasswordRequestForm = Depends()): # Weryfikacja użytkownika user = authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException(status_code=401, detail="Invalid credentials") access_token = create_access_token(data={"sub": user.username}) refresh_token = create_access_token(data={"sub": user.username, "type": "refresh"}) return { "access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer" } @app.post("/api/token/refresh/") async def refresh_token(refresh_token: str): try: payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM]) username = payload.get("sub") new_access_token = create_access_token(data={"sub": username}) return {"access_token": new_access_token, "token_type": "bearer"} except: raise HTTPException(status_code=401, detail="Invalid refresh token")
Frontend (React)
JavaScript1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68// api.js const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000'; class ApiClient { constructor() { this.baseURL = API_URL; } async login(username, password) { const formData = new FormData(); formData.append('username', username); formData.append('password', password); const response = await fetch(`${this.baseURL}/api/token/`, { method: 'POST', body: formData, }); if (!response.ok) { throw new Error('Invalid credentials'); } const data = await response.json(); localStorage.setItem('access_token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); return data; } async get(path) { const token = localStorage.getItem('access_token'); const response = await fetch(`${this.baseURL}${path}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (response.status === 401) { // Token wygasł, odśwież await this.refreshToken(); return this.get(path); // Ponów żądanie } return response.json(); } async refreshToken() { const refreshToken = localStorage.getItem('refresh_token'); const response = await fetch(`${this.baseURL}/api/token/refresh/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ refresh_token: refreshToken }), }); if (response.ok) { const data = await response.json(); localStorage.setItem('access_token', data.access_token); } else { // Wyloguj użytkownika localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); window.location.href = '/login'; } } } export default new ApiClient();
Komponent React
JavaScript1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38// Login.jsx import { useState } from 'react'; import apiClient from './api'; function Login() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); try { await apiClient.login(username, password); window.location.href = '/dashboard'; } catch (error) { alert('Login failed'); } }; return ( <form onSubmit={handleSubmit}> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> <button type="submit">Login</button> </form> ); } export default Login;
Dobre praktyki integracji
Poniżej znajdziesz zestaw najlepszych praktyk do integracji frontendu z backendem:
1. Ustal spójną konwencję nazw endpointów i struktur JSON
Używaj RESTful conventions:
- Rzeczowniki w liczbie mnogiej:
/api/posts/,/api/users/ - Czasowniki dla akcji:
/api/posts/1/publish/ - Spójne formaty dat: ISO 8601
2. Waliduj dane po obu stronach
Backend: Waliduj wszystkie dane wejściowe (Pydantic, Django serializers) Frontend: Waliduj przed wysłaniem (react-hook-form, yup)
3. Stosuj HTTPS i JWT z krótkim TTL
- HTTPS w produkcji (Let's Encrypt)
- Krótki czas życia access tokena (15-30 min)
- Długi refresh token (7-30 dni)
4. Używaj HTTP-only cookies dla refresh tokenów
Python1 2 3 4 5 6 7 8# FastAPI response.set_cookie( key="refresh_token", value=refresh_token, httponly=True, secure=True, samesite="lax" )
5. Włącz logowanie błędów API
Backend:
Python1 2 3 4 5 6 7 8import logging logger = logging.getLogger(__name__) try: # Kod except Exception as e: logger.error(f"API error: {e}") raise HTTPException(status_code=500, detail="Internal server error")
Frontend:
JavaScript1 2 3 4 5 6try { const data = await fetch('/api/posts'); } catch (error) { console.error('API Error:', error); // Wyślij do Sentry/LogRocket }
6. Testuj komunikację end-to-end
- Postman – testowanie API
- Cypress/Playwright – E2E testy frontendu
- Integration tests – testy całego flow
7. Używaj TypeScript dla lepszej integracji
TypeScript1 2 3 4 5 6 7 8 9 10 11 12 13// types.ts interface Post { id: number; title: string; author: string; created_at: string; } // api.ts async function getPosts(): Promise<Post[]> { const response = await fetch('/api/posts/'); return response.json(); }
8. Implementuj retry logic i error handling
JavaScript1 2 3 4 5 6 7 8 9 10 11async function fetchWithRetry(url, options, retries = 3) { for (let i = 0; i < retries; i++) { try { const response = await fetch(url, options); if (response.ok) return response; } catch (error) { if (i === retries - 1) throw error; await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); } } }
Podsumowanie
Połączenie Pythona (Django/FastAPI) z Reactem to dziś standard w nowoczesnych projektach webowych. Dzięki jasnym zasadom komunikacji (REST, JWT, CORS) możesz tworzyć skalowalne aplikacje SPA, wydajne API i elastyczny proces wdrożeniowy (DevOps-ready).
Najważniejsze punkty:
- CORS – włącz dla komunikacji między domenami
- JWT – bezpieczna autoryzacja bezstanowa
- Spójne API – RESTful endpoints i struktury JSON
- Dokumentacja – Swagger/OpenAPI dla łatwej współpracy
- Testowanie – E2E testy całego flow
- Wersjonowanie –
/api/v1/,/api/v2/dla kompatybilności wstecznej
Najważniejsze jest zachowanie spójności i przewidywalności – wtedy współpraca między frontendem i backendem staje się naturalna. Zacznij od podstawowej konfiguracji CORS i JWT, stopniowo dodawaj funkcjonalności i optymalizuj komunikację zgodnie z potrzebami projektu.



