Guía extensa para implementar proyectos FastAPI, Django con React/Next.js y despliegue en múltiples plataformas
Este manual cubre el proceso completo desde la creación inicial de proyectos backend con FastAPI y Django, integración con frontends modernos (React y Next.js), hasta el despliegue automatizado en diversas plataformas utilizando Docker y CI/CD.
Nota: Este documento asume conocimientos básicos de desarrollo web, Docker y sistemas de control de versiones. Cada sección incluye ejemplos prácticos y configuraciones detalladas.
La arquitectura recomendada para proyectos modernos sigue el patrón de microservicios o monolito modular, dependiendo de los requisitos.
fastapi-project/
├── app/
│ ├── __init__.py
│ ├── main.py # Punto de entrada principal
│ ├── api/ # Routers y endpoints
│ ├── models/ # Modelos Pydantic/SQLAlchemy
│ ├── schemas/ # Esquemas Pydantic
│ ├── db/ # Configuración de base de datos
│ ├── utils/ # Utilidades comunes
│ └── tests/ # Pruebas
├── requirements.txt # Dependencias Python
├── Dockerfile
├── docker-compose.yml
├── .env # Variables de entorno
└── README.md
django-project/
├── project/ # Configuración principal
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── asgi.py/wsgi.py
├── apps/
│ └── core/ # Aplicación de ejemplo
│ ├── migrations/
│ ├── __init__.py
│ ├── models.py
│ ├── views.py
│ └── serializers.py
├── manage.py
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
└── README.md
frontend/
├── public/ # Assets estáticos
├── src/
│ ├── components/ # Componentes reutilizables
│ ├── pages/ # Páginas (Next.js) o rutas
│ ├── styles/ # Estilos globales
│ ├── utils/ # Funciones utilitarias
│ ├── contexts/ # Contextos de React
│ ├── hooks/ # Hooks personalizados
│ └── api/ # Llamadas a la API backend
├── next.config.js # Config Next.js
├── package.json
├── Dockerfile
└── README.md
Crea un entorno virtual e instala FastAPI:
# Crear proyecto
mkdir fastapi-project && cd fastapi-project
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# Instalar dependencias
pip install fastapi uvicorn sqlalchemy psycopg2-binary python-dotenv
# Crear estructura básica
mkdir app && touch app/main.py
Contenido básico de app/main.py
:
from fastapi import FastAPI
from dotenv import load_dotenv
load_dotenv()
app = FastAPI(title="Mi API FastAPI")
@app.get("/")
def read_root():
return {"message": "Bienvenido a mi API"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
# Crear proyecto
mkdir django-project && cd django-project
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# Instalar Django
pip install django psycopg2-binary python-dotenv
# Crear proyecto y app
django-admin startproject project .
python manage.py startapp core
Configuración básica en project/settings.py
:
# settings.py
from dotenv import load_dotenv
import os
load_dotenv()
# Configuración de base de datos
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT'),
}
}
# Configuración de CORS (si se usa con frontend)
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
# Crear proyecto Next.js
npx create-next-app@latest frontend --typescript
cd frontend
# Instalar dependencias comunes
npm install axios react-query @tanstack/react-query @mui/material @emotion/react @emotion/styled
Configuración básica para conectar con el backend en src/utils/api.ts
:
import axios from 'axios';
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
headers: {
'Content-Type': 'application/json',
},
});
// Interceptor para manejar errores
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
console.error('Error de API:', error.response.data);
} else {
console.error('Error de conexión:', error.message);
}
return Promise.reject(error);
}
);
export default api;
La containerización con Docker permite empaquetar aplicaciones con todas sus dependencias para un despliegue consistente.
# Usar imagen oficial de Python
FROM python:3.9-slim
# Establecer directorio de trabajo
WORKDIR /app
# Copiar requirements primero para cachear
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copiar el resto de la aplicación
COPY . .
# Variables de entorno
ENV PYTHONPATH=/app
ENV PORT=8000
# Exponer puerto
EXPOSE $PORT
# Comando para ejecutar la aplicación
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
# Usar imagen oficial de Python
FROM python:3.9-slim
# Establecer variables de entorno
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Establecer directorio de trabajo
WORKDIR /app
# Instalar dependencias del sistema
RUN apt-get update && apt-get install -y \
gcc \
python3-dev \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Instalar dependencias de Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copiar proyecto
COPY . .
# Comando para ejecutar la aplicación
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "project.wsgi:application"]
# Fase de construcción
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Fase de producción
FROM node:16-alpine
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["npm", "start"]
Archivo completo para desarrollo con backend, frontend y base de datos:
version: '3.8'
services:
db:
image: postgres:13
environment:
POSTGRES_USER: ${DB_USER:-postgres}
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
POSTGRES_DB: ${DB_NAME:-appdb}
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
backend:
build:
context: .
dockerfile: Dockerfile
command: uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
volumes:
- .:/app
environment:
- DB_HOST=db
- DB_USER=${DB_USER:-postgres}
- DB_PASSWORD=${DB_PASSWORD:-postgres}
- DB_NAME=${DB_NAME:-appdb}
- DB_PORT=5432
ports:
- "8000:8000"
depends_on:
db:
condition: service_healthy
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
command: npm run dev
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- NEXT_PUBLIC_API_URL=http://backend:8000
ports:
- "3000:3000"
depends_on:
- backend
volumes:
postgres_data:
Nota: Para producción, se recomienda configurar volúmenes separados para datos persistentes, optimizar las imágenes y configurar recursos límite.
Configuración con SQLAlchemy y Alembic para migraciones:
# app/db/base.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@db:5432/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Modelo de ejemplo:
# app/models/item.py
from sqlalchemy import Column, Integer, String
from app.db.base import Base
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
description = Column(String, index=True)
Configuración en settings.py
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME', 'appdb'),
'USER': os.getenv('DB_USER', 'postgres'),
'PASSWORD': os.getenv('DB_PASSWORD', 'postgres'),
'HOST': os.getenv('DB_HOST', 'db'),
'PORT': os.getenv('DB_PORT', '5432'),
}
}
Modelo de ejemplo:
# apps/core/models.py
from django.db import models
class Item(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
def __str__(self):
return self.title
Configuración con Motor (async) para FastAPI:
# app/db/mongodb.py
from motor.motor_asyncio import AsyncIOMotorClient
from dotenv import load_dotenv
import os
load_dotenv()
MONGO_DETAILS = os.getenv("MONGO_URI")
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client.get_database("appdb")
# Colecciones
items_collection = database.get_collection("items")
Configuración en src/_app.tsx
(Next.js):
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutos
cacheTime: 15 * 60 * 1000, // 15 minutos
retry: 1,
},
},
})
function MyApp({ Component, pageProps }) {
return (
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
Hook personalizado para llamadas API:
// src/hooks/useItems.ts
import { useQuery } from '@tanstack/react-query'
import api from '../utils/api'
const fetchItems = async () => {
const { data } = await api.get('/items')
return data
}
export const useItems = () => {
return useQuery(['items'], fetchItems)
}
Backend (FastAPI):
# app/api/auth.py
from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional
router = APIRouter()
# Configuración
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Funciones de autenticación
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
@router.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Verificar usuario y contraseña (implementar según tu DB)
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=400,
detail="Usuario o contraseña incorrectos"
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
Frontend (React/Next.js):
// src/context/AuthContext.tsx
import { createContext, useContext, ReactNode, useState, useEffect } from 'react'
import api from '../utils/api'
import { useRouter } from 'next/router'
type AuthContextType = {
user: any
login: (email: string, password: string) => Promise<void>
logout: () => void
isAuthenticated: boolean
}
const AuthContext = createContext<AuthContextType>({} as AuthContextType)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<any>(null)
const router = useRouter()
useEffect(() => {
async function loadUser() {
const token = localStorage.getItem('token')
if (token) {
try {
api.defaults.headers.common['Authorization'] = `Bearer ${token}`
const { data } = await api.get('/users/me')
setUser(data)
} catch (err) {
logout()
}
}
}
loadUser()
}, [])
const login = async (email: string, password: string) => {
const { data } = await api.post('/token', {
username: email,
password,
})
localStorage.setItem('token', data.access_token)
api.defaults.headers.common['Authorization'] = `Bearer ${data.access_token}`
const userResponse = await api.get('/users/me')
setUser(userResponse.data)
router.push('/dashboard')
}
const logout = () => {
localStorage.removeItem('token')
delete api.defaults.headers.common['Authorization']
setUser(null)
router.push('/login')
}
return (
<AuthContext.Provider
value={{
user,
login,
logout,
isAuthenticated: !!user,
}}
>
{children}
</AuthContext.Provider>
)
}
export const useAuth = () => useContext(AuthContext)
Configuración básica con pytest:
# tests/test_main.py
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Bienvenido a mi API"}
def test_read_item():
response = client.get("/items/42?q=test")
assert response.status_code == 200
assert response.json() == {"item_id": 42, "q": "test"}
# core/tests/test_models.py
from django.test import TestCase
from core.models import Item
class ItemModelTest(TestCase):
@classmethod
def setUpTestData(cls):
Item.objects.create(title='Test item', description='Test description')
def test_title_content(self):
item = Item.objects.get(id=1)
expected_object_name = f'{item.title}'
self.assertEqual(expected_object_name, 'Test item')
// src/components/__tests__/ItemList.test.tsx
import { render, screen } from '@testing-library/react'
import ItemList from '../ItemList'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const queryClient = new QueryClient()
describe('ItemList', () => {
it('renders loading state', () => {
render(
<QueryClientProvider client={queryClient}>
<ItemList />
</QueryClientProvider>
)
expect(screen.getByText('Loading...')).toBeInTheDocument()
})
})
# .github/workflows/main.yml
name: FastAPI CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:13
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests
env:
DB_HOST: localhost
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAME: testdb
run: |
pytest --cov=app --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v1
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/fastapi-app:latest
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
services:
- docker:dind
test:
stage: test
image: python:3.9
services:
- postgres:13
variables:
POSTGRES_DB: testdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DB_HOST: postgres
before_script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
script:
- pytest --cov=project --cov-report=xml
artifacts:
reports:
cobertura: coverage.xml
build:
stage: build
image: docker:latest
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:latest .
- docker push $CI_REGISTRY_IMAGE:latest
deploy:
stage: deploy
image: alpine:latest
needs: ["build"]
script:
- apk add --no-cache openssh-client rsync
- echo "$SSH_PRIVATE_KEY" > ssh_key
- chmod 600 ssh_key
- ssh -i ssh_key -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "docker pull $CI_REGISTRY_IMAGE:latest && docker-compose up -d"
# .github/workflows/nextjs.yml
name: Next.js CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
scope: ${{ secrets.VERCEL_ORG_ID }}
Configuración para desplegar en Render:
gunicorn --bind 0.0.0.0:$PORT project.wsgi
)Ejemplo de render.yaml
:
services:
- type: web
name: django-app
env: python
buildCommand: pip install -r requirements.txt
startCommand: gunicorn --bind 0.0.0.0:$PORT project.wsgi
envVars:
- key: DATABASE_URL
fromDatabase:
name: appdb
property: connectionString
Para frontends Next.js:
Para backends (usando funciones serverless):
// api/hello.js
module.exports = (req, res) => {
res.json({ message: 'Hello from Vercel!' })
}
Configuración con Docker:
flyctl launch
fly.toml
:# fly.toml
app = "your-app-name"
[build]
image = "your-docker-image"
[env]
PORT = "8000"
[[services]]
internal_port = 8000
protocol = "tcp"
[services.concurrency]
hard_limit = 25
soft_limit = 20
[[services.ports]]
handlers = ["http"]
port = "80"
[[services.ports]]
handlers = ["tls", "http"]
port = "443"
Despliegue con ECS:
Ejemplo de task definition:
{
"family": "fastapi-app",
"networkMode": "awsvpc",
"executionRoleArn": "arn:aws:iam::account-id:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "fastapi-container",
"image": "account-id.dkr.ecr.region.amazonaws.com/fastapi-app:latest",
"portMappings": [
{
"containerPort": 8000,
"hostPort": 8000,
"protocol": "tcp"
}
],
"essential": true,
"environment": [
{"name": "DB_HOST", "value": "your-rds-endpoint"},
{"name": "DB_NAME", "value": "appdb"}
],
"secrets": [
{"name": "DB_PASSWORD", "valueFrom": "arn:aws:ssm:region:account-id:parameter/db-password"}
]
}
],
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048"
}
Configuración manual con Docker:
Ejemplo de configuración Nginx:
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias /path/to/your/static/files;
}
}
Configuración con Serverless Framework:
# serverless.yml
service: fastapi-serverless
provider:
name: aws
runtime: python3.9
region: us-east-1
memorySize: 512
timeout: 30
functions:
app:
handler: wsgi_handler.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
plugins:
- serverless-python-requirements
- serverless-wsgi
custom:
wsgi:
app: app.main.app
packRequirements: false
pythonRequirements:
dockerizePip: true
Adaptador WSGI para Lambda:
# wsgi_handler.py
from mangum import Mangum
from app.main import app
handler = Mangum(app)
# app/middleware/logging.py
import logging
import time
from fastapi import Request
logger = logging.getLogger(__name__)
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = (time.time() - start_time) * 1000
formatted_time = "{0:.2f}".format(process_time)
logger.info(
f"method={request.method} path={request.url.path} "
f"status_code={response.status_code} "
f"processed_in={formatted_time}ms"
)
return response
# settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="your-sentry-dsn",
integrations=[DjangoIntegration()],
traces_sample_rate=1.0,
send_default_pii=True
)
// src/lib/logrocket.js
import LogRocket from 'logrocket'
import setupLogRocketReact from 'logrocket-react'
if (process.env.NODE_ENV === 'production') {
LogRocket.init('your-app-id')
setupLogRocketReact(LogRocket)
}
export default LogRocket
Configuración para FastAPI:
# app/monitoring/prometheus.py
from prometheus_client import make_asgi_app, Counter, Histogram
REQUEST_COUNT = Counter(
'request_count', 'App Request Count',
['method', 'endpoint', 'http_status']
)
REQUEST_LATENCY = Histogram(
'request_latency_seconds', 'Request latency',
['method', 'endpoint']
)
def monitor_request(request, response):
REQUEST_COUNT.labels(
request.method, request.url.path, response.status_code
).inc()
def record_latency(request, latency):
REQUEST_LATENCY.labels(
request.method, request.url.path
).observe(latency)
Configuración de docker-compose para monitoreo:
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
grafana:
image: grafana/grafana
ports:
- "3001:3000"
volumes:
- grafana-storage:/var/lib/grafana
depends_on:
- prometheus
volumes:
grafana-storage:
# Ejemplo de Dockerfile seguro
FROM python:3.9-slim
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "project.wsgi"]
# settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_SECONDS = 31536000 # 1 año
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
app = FastAPI()
app.add_middleware(HTTPSRedirectMiddleware)
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["example.com", "*.example.com"]
)
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-XSS-Protection',
value: '1; mode=block'
}
]
}
]
}
}
# FastAPI con cache
from fastapi import FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from redis import asyncio as aioredis
app = FastAPI()
@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://redis")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
// Next.js dynamic import
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(
() => import('../components/HeavyComponent'),
{ loading: () => <p>Loading...</p>, ssr: false }
)
# Django connection pooling
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'user',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '5432',
'OPTIONS': {
'MAX_CONNS': 20,
}
}
}
// next.config.js - Headers de cache
module.exports = {
async headers() {
return [
{
source: '/_next/static/(.*)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
]
},
}
Este manual ha cubierto el ciclo completo de desarrollo y despliegue de aplicaciones web modernas, desde la configuración inicial hasta el monitoreo en producción. Las tecnologías presentadas (FastAPI, Django, React, Next.js) combinadas con las estrategias de despliegue (Docker, CI/CD, múltiples plataformas) proporcionan una base sólida para construir aplicaciones escalables y mantenibles.