diff --git a/.github/actions/save-coverage/action.yml b/.github/actions/save-coverage/action.yml deleted file mode 100644 index 24416de..0000000 --- a/.github/actions/save-coverage/action.yml +++ /dev/null @@ -1,186 +0,0 @@ -name: "Save Coverage Artifacts" -description: "Guarda los reportes de cobertura en la rama de artefactos" - -inputs: - gh-token: - description: "GitHub Token para poder pushear a la rama de artefactos" - required: true - artifacts-branch: - description: "Nombre de la rama donde se subirán los artefactos (reportes de cobertura, etc.)" - required: false - default: "artifacts" - coverage-source: - description: "Ruta relativa al workspace donde se encuentran los reportes, o si is-artifact es true, nombre (o ruta) del archivo ZIP" - required: false - default: "coverage-reports" - is-artifact: - description: "Si es true, indica que el reporte viene de un artifact y se debe descargar; en caso contrario se copiará la carpeta directamente" - required: false - default: "false" - -runs: - using: "composite" - steps: - # ---------------------------------------------------------------------------------------- - # Paso 1: Hacer checkout del código en la carpeta "action-repo" - # ---------------------------------------------------------------------------------------- - - name: "Checkout code" - uses: actions/checkout@v3 - with: - path: "action-repo" - - # ---------------------------------------------------------------------------------------- - # Paso 2A (si is-artifact es true): Descargar el artifact de cobertura - # ---------------------------------------------------------------------------------------- - - name: "Descargar artifact de cobertura" - if: ${{ inputs.is-artifact == 'true' }} - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.coverage-source }} - path: downloaded-coverage - - # ---------------------------------------------------------------------------------------- - # Paso 2B: Verificar el contenido de la cobertura - # - Si is-artifact es true se revisa "downloaded-coverage". - # - Si is-artifact es false se revisa el folder "coverage-source" directamente. - # ---------------------------------------------------------------------------------------- - - name: "Verificar artifact descargado de cobertura" - if: ${{ inputs.is-artifact == 'true' }} - shell: bash - env: - WORKSPACE_PATH: ${{ github.workspace }} - run: | - set -e - echo "▶️ Verificando artifact descargado..." - cd "${WORKSPACE_PATH}/downloaded-coverage" - if [ ! "$(ls -A .)" ]; then - echo "❌ Error: La carpeta 'downloaded-coverage' está vacía." - exit 1 - fi - echo "✅ Artifact descargado y contiene archivos. Listando contenido:" - ls -lahR . - echo "REPORTS_PATH=${WORKSPACE_PATH}/downloaded-coverage" >> "$GITHUB_ENV" - - - name: "Verificar carpeta de cobertura" - if: ${{ inputs.is-artifact != 'true' }} - shell: bash - env: - WORKSPACE_PATH: ${{ github.workspace }} - REPORTS_PATH: ${{ inputs.coverage-source }} - run: | - set -e - echo "▶️ Verificando la carpeta de cobertura..." - cd "${WORKSPACE_PATH}" - echo "🔍 Revisando la carpeta '${REPORTS_PATH}' en '${WORKSPACE_PATH}'" - if [ ! -d "${REPORTS_PATH}" ]; then - echo "❌ Error: La carpeta '${WORKSPACE_PATH}/${REPORTS_PATH}' no existe." - exit 1 - fi - FILE_COUNT=$(find "${REPORTS_PATH}" | wc -l) - if [ "${FILE_COUNT}" -le 1 ]; then - echo "❌ Error: La carpeta '${WORKSPACE_PATH}/${REPORTS_PATH}' está vacía." - exit 1 - fi - echo "✅ La carpeta '${REPORTS_PATH}' existe y contiene archivos. Listando contenido:" - ls -lahR "${REPORTS_PATH}" - echo "REPORTS_PATH=${WORKSPACE_PATH}/${REPORTS_PATH}" >> "$GITHUB_ENV" - - # ---------------------------------------------------------------------------------------- - # Paso 3: Clonar (o crear) la rama de artefactos en la carpeta "artifacts-repo" - # ---------------------------------------------------------------------------------------- - - name: "Clonar o crear la rama de artefactos" - shell: bash - env: - GH_TOKEN: ${{ inputs.gh-token }} - COMMIT_ID: ${{ github.sha }} - BRANCH_NAME: ${{ github.ref_name }} - REPO_NAME: ${{ github.repository }} - ARTIFACTS_BRANCH: ${{ inputs.artifacts-branch }} - WORKSPACE_PATH: ${{ github.workspace }} - run: | - set -e - echo "▶️ Iniciando clonación o creación de la rama '${ARTIFACTS_BRANCH}'" - # Configurar Git - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global init.defaultBranch "${ARTIFACTS_BRANCH}" - - echo "🔎 Verificando si la rama '${ARTIFACTS_BRANCH}' ya existe en remoto" - BRANCH_EXISTS=$(git ls-remote --heads "https://x-access-token:${GH_TOKEN}@github.com/${REPO_NAME}.git" "${ARTIFACTS_BRANCH}") - - if [ -n "${BRANCH_EXISTS}" ]; then - echo "✅ La rama '${ARTIFACTS_BRANCH}' existe. Clonándola en la carpeta 'artifacts-repo'." - git clone --branch "${ARTIFACTS_BRANCH}" --single-branch "https://x-access-token:${GH_TOKEN}@github.com/${REPO_NAME}.git" "artifacts-repo" - else - echo "🆕 La rama '${ARTIFACTS_BRANCH}' NO existe. Creándola como rama huérfana." - mkdir -p "artifacts-repo" - cd "artifacts-repo" - git init - git checkout --orphan "${ARTIFACTS_BRANCH}" - git commit --allow-empty -m "Commit inicial para la rama ${ARTIFACTS_BRANCH}" - git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${REPO_NAME}.git" - git push --set-upstream origin "${ARTIFACTS_BRANCH}" - cd .. - fi - echo "📁 Directorio final: $(pwd). Si existe 'artifacts-repo', se clonó o se inicializó." - - # ---------------------------------------------------------------------------------------- - # Paso 4: Copiar los reportes de cobertura desde la carpeta REPORTS_PATH (descargada o local) - # a 'artifacts-repo' y hacer commit de los artefactos. - # ---------------------------------------------------------------------------------------- - - name: "Copiar cobertura y hacer commit" - shell: bash - env: - GH_TOKEN: ${{ inputs.gh-token }} - COMMIT_ID: ${{ github.sha }} - BRANCH_NAME: ${{ github.ref_name }} - REPO_NAME: ${{ github.repository }} - ARTIFACTS_BRANCH: ${{ inputs.artifacts-branch }} - WORKSPACE_PATH: ${{ github.workspace }} - run: | - set -e - # Verificar que la variable REPORTS_PATH esté definida (desde pasos anteriores) - if [ -z "${REPORTS_PATH}" ]; then - echo "❌ Error: La variable REPORTS_PATH no está definida." - exit 1 - fi - echo "Usando REPORTS_PATH=${REPORTS_PATH}" - - # Ubicarnos en la carpeta clonada de la rama de artefactos - if [ -d "artifacts-repo" ]; then - cd "artifacts-repo" - else - echo "⚠️ No se encontró 'artifacts-repo'. Revisar si hubo error previo en la clonación." - exit 1 - fi - - echo "▶️ Creando directorios para guardar la cobertura en 'artifacts-repo'." - mkdir -p "${BRANCH_NAME}/${COMMIT_ID}" - mkdir -p "${BRANCH_NAME}/latest" - - echo "🔀 Regresando a '${WORKSPACE_PATH}' para copiar la cobertura." - cd "${WORKSPACE_PATH}" - - echo "📂 Copiando contenido de '${REPORTS_PATH}' a 'artifacts-repo/${BRANCH_NAME}/${COMMIT_ID}'" - cp -r "${REPORTS_PATH}/." "${WORKSPACE_PATH}/artifacts-repo/${BRANCH_NAME}/${COMMIT_ID}/" - cp -r "${REPORTS_PATH}/." "${WORKSPACE_PATH}/artifacts-repo/${BRANCH_NAME}/latest/" - - echo "✅ Cobertura copiada. Preparando commit..." - cd "${WORKSPACE_PATH}/artifacts-repo" - - echo "📝 Creando (o editando) README.md" - echo "# Repositorio de Artefactos" > README.md - echo "" >> README.md - echo "Contiene reportes de test y otros artefactos del proyecto." >> README.md - - echo "📦 Agregando todos los cambios al staging." - git add . - - COMMIT_MESSAGE="Agregar artefactos y README para commit ${COMMIT_ID} en la rama ${BRANCH_NAME}" - echo "📦 Haciendo commit con mensaje: '${COMMIT_MESSAGE}'" - git commit -m "${COMMIT_MESSAGE}" || echo "⚠️ No hay cambios nuevos que comitear." - - echo "🚀 Haciendo push a la rama '${ARTIFACTS_BRANCH}'" - git push origin "${ARTIFACTS_BRANCH}" - - echo "✅ Artefactos subidos exitosamente a la rama '${ARTIFACTS_BRANCH}'." diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a276a76..6de5bc8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -125,7 +125,7 @@ jobs: path: ${{ env.COVERAGE_REPORTS }} - name: Guardar coverage - uses: ./.github/actions/save-coverage + uses: ronihdzz/git-archive-action@v3 with: gh-token: ${{ secrets.GITHUB_TOKEN }} artifacts-branch: 'artifacts' diff --git a/README.es.md b/README.es.md new file mode 100644 index 0000000..de389ce --- /dev/null +++ b/README.es.md @@ -0,0 +1,370 @@ +# AWS Lambda FastAPI Template + +[![English](https://img.shields.io/badge/🇺🇸-English-blue)](README.md) [![Español](https://img.shields.io/badge/🇪🇸-Español-red)](README.es.md) + +| Ambiente | Cobertura | +|-----------|----------| +| main | ![Coverage Badge](https://github.com/ronihdzz/aws-lambda-fastapi/blob/artifacts/main/latest/coverage.svg) | +| development | ![Coverage Badge](https://github.com/ronihdzz/aws-lambda-fastapi/blob/artifacts/development/latest/coverage.svg) | + +## 📋 Descripción del Template + +Este es un template completo para el desarrollo de APIs REST con **FastAPI** diseñado para ser deployado en **AWS Lambda** usando **Mangum** como proxy inverso. El template implementa un enfoque **serverless** completo, proporcionando una arquitectura modular, manejo de errores estandarizado, testing automatizado contra bases de datos reales, y un stack integral de CI/CD con Docker. + +### 🚀 **Contexto Serverless y AWS Lambda** + +El template está optimizado para **arquitecturas serverless** usando AWS Lambda: +- **Mangum** actúa como adaptador/proxy inverso que permite que FastAPI funcione como una función Lambda +- **Serverless Computing**: Sin gestión de servidores, escalado automático, pago por uso +- **API Gateway Integration**: Compatible con AWS API Gateway para routing y autenticación +- **Cold Start Optimization**: Estructura optimizada para minimizar el tiempo de arranque en frío + +## 🏗️ Arquitectura y Estructura del Proyecto + +### Estructura de Directorios + +``` +aws-lambda-fastapi/ +├── src/ # Código fuente principal +│ ├── api/ # Capa de API - Routers y endpoints +│ │ ├── v1/ # Versioning de API +│ │ ├── routers.py # Registro de routers principales +│ │ └── endpoints.py # Endpoints base (health check, etc.) +│ ├── core/ # Configuración central +│ │ ├── settings/ # Configuraciones por ambiente +│ │ ├── exceptions.py # Excepciones específicas del dominio +│ │ └── internal_codes.py # Códigos internos del dominio +│ ├── shared/ # Utilidades compartidas +│ │ ├── middlewares/ # Middlewares personalizados +│ │ ├── base_responses.py # Respuestas estandarizadas +│ │ ├── base_internal_codes.py # Códigos internos base +│ │ ├── base_exceptions.py # Excepciones base +│ │ └── utils_dates.py # Utilidades de fechas +│ ├── db/ # Capa de base de datos +│ │ ├── postgresql/ # Configuración PostgreSQL +│ │ └── mongo/ # Configuración MongoDB +│ ├── tests/ # Suite de pruebas +│ └── main.py # Punto de entrada de la aplicación +├── docker_images/ # Imágenes Docker personalizadas +│ └── testing/ # Configuración para testing +├── .github/workflows/ # Pipelines CI/CD +└── docs/ # Documentación del proyecto +``` + +## 🔧 Componentes Principales + +### 1. **Sistema de Respuestas Estandarizadas** (`@/shared/base_responses.py`) + +El template implementa un sistema de respuestas consistente usando un patrón **Envelope Response**: + +```python +class EnvelopeResponse(BaseModel): + success: bool + message: str + data: dict[str, Any] | list | None = None + trace_id: str | None = None +``` + +**Convenciones:** +- Todas las respuestas siguen el mismo formato +- Manejo automático de serialización de Pydantic models +- Inclusión de trace_id para debugging +- Respuestas de error estructuradas con códigos internos + +### 2. **Sistema de Códigos Internos** (`@/shared/base_internal_codes.py`) + +Sistema de códigos de error estandarizado para tracking y debugging: + +```python +class CommonInternalCode(InternalCodeBase): + UNKNOWN = 100, "Unknown error" + PYDANTIC_VALIDATIONS_REQUEST = 8001, "Failed Pydantic validations on request" +``` + +**Convenciones:** +- Códigos numéricos únicos para cada tipo de error +- Descripción clara del error +- Extensible para códigos específicos del dominio en `@/core/internal_codes.py` + +### 3. **Middleware de Manejo de Errores** (`@/shared/middlewares/`) + +#### CatcherExceptions +Middleware principal que captura todas las excepciones: +- Manejo de HTTPException de FastAPI +- Manejo de NoResultFound de SQLAlchemy +- Manejo de excepciones personalizadas +- Conversión automática a formato de respuesta estandarizado + +#### CatcherExceptionsPydantic +Middleware específico para errores de validación de Pydantic: +- Captura errores de validación de request body +- Formato consistente de errores de validación + +### 4. **Sistema de Configuración** (`@/core/settings/`) + +Configuración por ambientes usando Pydantic Settings: + +``` +settings/ +├── base.py # Configuración base +├── local.py # Desarrollo local +├── development.py # Ambiente de desarrollo +├── staging.py # Ambiente de staging +├── production.py # Ambiente de producción +└── testing.py # Configuración para tests +``` + +**Convenciones:** +- Variables de entorno tipadas +- Validación automática de configuración +- Configuración específica por ambiente +- Soporte para múltiples bases de datos + +### 5. **Sistema Multi-Base de Datos** (`@/db/`) + +Template preparado para múltiples gestores de base de datos con **ejemplos funcionales**: + +#### PostgreSQL (`@/db/postgresql/`) +``` +postgresql/ +├── base.py # Clase base para modelos SQLAlchemy +├── connection.py # Configuración de conexión +├── models/ +│ └── public/ # Esquema 'public' +│ └── book.py # Modelo de ejemplo +└── __init__.py # Exports principales +``` + +- **SQLAlchemy 2.0**: ORM moderno con syntax moderna +- **Modelos tipados**: Full type hints y validación +- **Conexión singleton**: Reutilizable y eficiente + +#### MongoDB (`@/db/mongo/`) +``` +mongo/ +├── base.py # Clase base para documentos +├── connection.py # Cliente MongoDB configurado +├── models/ # Modelos de documentos +└── __init__.py # Exports principales +``` + +- **PyMongo**: Driver oficial de MongoDB +- **Pydantic Integration**: Modelos con validación automática +- **Async Ready**: Preparado para operaciones asíncronas + +**Convenciones:** +- Modelos organizados por esquemas/colecciones +- Conexiones singleton pattern +- Configuración específica por ambiente +- **Extensible**: Fácil agregar Redis, DynamoDB, etc. + +### 6. **Estructura de API Modular** (`@/api/`) + +Sistema de versionado y organización modular por dominio: + +``` +api/ +├── v1/ # Versión 1 de la API +│ └── books/ # Módulo de dominio (ejemplo) +│ ├── endpoints.py # Define los endpoints REST +│ ├── repositories.py # Acceso a la base de datos +│ ├── schema.py # Modelos Pydantic (DTOs) +│ └── services.py # Lógica de negocio +├── routers.py # Registro de routers globales +└── endpoints.py # Endpoints base (health, index) +``` + +**Convenciones del Módulo:** +- **endpoints.py**: Definición de rutas y validaciones de entrada +- **services.py**: Lógica de negocio, orquestación +- **repositories.py**: Acceso a datos, queries específicas +- **schema.py**: DTOs (Data Transfer Objects) con Pydantic + +**Escalabilidad**: Puedes añadir más módulos (`users/`, `orders/`, etc.) siguiendo la misma estructura. + +### 7. **Sistema de Testing** (`@/tests/`) + +Suite completa de testing automatizado: + +``` +tests/ +├── common/ # Utilities comunes para tests +├── utils/ # Utilidades de testing +├── v1/ # Tests específicos por versión +└── __init__.py # Configuración de base de datos para tests +``` + +**Convenciones:** +- **Base de datos de testing separada**: Configuración automática via `environment_testing` +- **Fixtures reutilizables**: Utilities comunes en `tests/common/` +- **Cobertura automatizada**: Integrada en CI/CD con reportes detallados +- **Tests organizados por versión**: Estructura modular por versión de API +- **Setup automático**: El script `prepare_database()` crea esquemas y tablas necesarios cada vez que el runner arranca + +### 8. **Stack Docker Integral** (`@/docker_images/`) + +El proyecto organiza de forma integral las imágenes Docker para diferentes propósitos: + +#### **Testing Docker** (`@/docker_images/testing/`) +Configuración especializada para testing contra **bases de datos reales** (no mocks): + +- **`Dockerfile.testing`**: Imagen optimizada para ejecutar tests +- **`ci.env.sh`**: Script que configura variables de entorno para CI/CD +- **`entrypoint.sh`**: Orquesta la ejecución de tests y generación de reportes + +**Selección de Bases de Datos en CI:** +```bash +# En ci.env.sh puedes seleccionar qué bases de datos levantar +export POSTGRESQL_URL="${GITHUB_DATABASE_POSTGRESQL:-$POSTGRESQL_URL}" +export MONGO_URL="${GITHUB_DATABASE_MONGODB:-$MONGO_URL}" +export REDIS_URL="${GITHUB_DATABASE_REDIS:-$REDIS_URL}" +``` + +Los tests se ejecutan contra **instanciones reales** de PostgreSQL, MongoDB y Redis en el runner de GitHub Actions. + +#### **Build y Deploy Docker** +- **`docker_images/build/`**: Imagen para construcción optimizada +- **`docker_images/deploy/`**: Imagen final para deployment en AWS Lambda + +## 🚀 Pipelines CI/CD Completos + +### **Flujo Principal de GitHub Actions** + +El pipeline ejecuta una secuencia completa y robusta: + +#### **1. 🔍 Linting (Pre-commit)** +```yaml +- Ejecuta pre-commit para linting (configurado en .pre-commit-config.yaml) +- Formateo de código con Black +- Linting con Flake8 +- Type checking con MyPy +- Validación de imports y estructura +``` + +#### **2. 🧪 Testing con Bases de Datos Reales** +```yaml +services: + postgres: # PostgreSQL 13 en puerto 5432 + mongodb: # MongoDB 4.4 en puerto 27017 + redis: # Redis 6 en puerto 6379 +``` +- Levanta servicios reales de bases de datos +- Ejecuta tests contra instancias reales (no mocks) +- Genera reportes de cobertura detallados + +#### **3. 📊 Cobertura y Badges** +- **Generación**: `coverage run`, `coverage xml`, `coverage-badge` +- **Archivado**: Usa `ronihdzz/git-archive-action@v3` para guardar artefactos +- **Storage**: Los reportes se almacenan en rama dedicada `artifacts` +- **Badges**: Se generan badges dinámicos de % de cobertura mostrados en README + +#### **4. 🏗️ Build y Deploy** +- Construcción de imagen Docker optimizada para Lambda +- Push a Amazon ECR (Elastic Container Registry) +- Deploy automático a AWS Lambda +- Versionado automático por ambiente (dev/staging/prod) + +### **Configuración de Ambientes** + +```yaml +BRANCH_ENV_MAP: + "main": "prod" + "development": "dev" + "staging": "stg" +``` + +Cada rama mapea automáticamente a su ambiente correspondiente. + +## 📦 Dependencias y Tecnologías + +### Core Dependencies +- **FastAPI**: Framework web moderno y rápido +- **Mangum**: Adaptador para AWS Lambda +- **Pydantic**: Validación de datos y settings +- **SQLAlchemy**: ORM para bases de datos relacionales +- **PyMongo**: Driver para MongoDB +- **Loguru**: Logging avanzado + +### Development Dependencies +- **Pytest**: Framework de testing +- **Coverage**: Cobertura de código +- **MyPy**: Type checking +- **Pre-commit**: Hooks de pre-commit + +## 🛠️ Comandos de Desarrollo + +### Ejecutar el proyecto localmente +```bash +uvicorn main:app --reload --port=9595 +``` + +### Ejecutar tests +```bash +pytest src/tests/ -v --cov=src +``` + +### Ejecutar pre-commit hooks +```bash +pre-commit run --all-files +``` + +### Type checking +```bash +mypy src/ +``` + +### Testing con Docker +```bash +docker-compose -f docker-compose.yml up testing +``` + +## 🔍 Testing de Lambda Localmente + +Para probar la función Lambda localmente: + +```bash +curl -X POST "http://localhost:9100/2015-03-31/functions/function/invocations" \ + -H "Content-Type: application/json" \ + -d '{ + "version": "2.0", + "routeKey": "GET /", + "rawPath": "/", + "rawQueryString": "", + "headers": {}, + "requestContext": { + "http": { + "method": "GET", + "path": "/", + "sourceIp": "127.0.0.1" + } + }, + "isBase64Encoded": false + }' +``` + + +## 🎯 **Ventajas del Template** + +### **Para Desarrollo** +- **Serverless Ready**: Optimizado para AWS Lambda con Mangum +- **Multi-Database**: Soporte nativo para PostgreSQL, MongoDB, Redis +- **Type Safety**: Full TypeScript-like experience con MyPy +- **Error Handling**: Sistema robusto de manejo de errores con códigos internos + +### **Para Testing** +- **Real Databases**: Tests contra bases de datos reales, no mocks +- **Automated Setup**: Creación automática de esquemas y datos de prueba +- **Coverage Tracking**: Reportes detallados con badges automáticos +- **Docker Isolated**: Ambiente de testing completamente aislado + +### **Para CI/CD** +- **Full Pipeline**: Linting → Testing → Coverage → Build → Deploy +- **Multi-Environment**: Soporte automático para dev/staging/prod +- **Quality Gates**: Pre-commit hooks y validaciones automáticas +- **Artifact Management**: Storage automático de reportes y badges + +### **Para Producción** +- **AWS Optimized**: Configurado para AWS Lambda + API Gateway +- **Environment Config**: Configuración tipada por ambiente +- **Monitoring Ready**: Logging estructurado con Loguru + Sentry +- **Scalable Architecture**: Estructura modular fácil de escalar diff --git a/README.md b/README.md index 9db1f9a..9668e91 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,329 @@ -# aws-lambda-fastapi +# AWS Lambda FastAPI Template -| CI Environment | Coverage | +[![English](https://img.shields.io/badge/🇺🇸-English-blue)](README.md) [![Español](https://img.shields.io/badge/🇪🇸-Español-red)](README.es.md) + +| Environment | Coverage | |-----------|----------| -| main| ![Coverage Badge](https://github.com/ronihdzz/aws-lambda-fastapi/blob/artifacts/main/latest/coverage.svg) | -| development| ![Coverage Badge](https://github.com/ronihdzz/aws-lambda-fastapi/blob/artifacts/development/latest/coverage.svg) | +| main | ![Coverage Badge](https://github.com/ronihdzz/aws-lambda-fastapi/blob/artifacts/main/latest/coverage.svg) | +| development | ![Coverage Badge](https://github.com/ronihdzz/aws-lambda-fastapi/blob/artifacts/development/latest/coverage.svg) | + +## 📋 Template Description + +This is a complete template for developing REST APIs with **FastAPI** designed to be deployed on **AWS Lambda** using **Mangum** as a reverse proxy. The template implements a complete **serverless** approach, providing modular architecture, standardized error handling, automated testing against real databases, and a comprehensive CI/CD stack with Docker. +### 🚀 **Serverless & AWS Lambda Context** -## Run project +The template is optimized for **serverless architectures** using AWS Lambda: +- **Mangum** acts as an adapter/reverse proxy that allows FastAPI to function as a Lambda function +- **Serverless Computing**: No server management, automatic scaling, pay-per-use +- **API Gateway Integration**: Compatible with AWS API Gateway for routing and authentication +- **Cold Start Optimization**: Structure optimized to minimize cold start time + +## 🏗️ Architecture and Project Structure + +### Directory Structure ``` -uvicorn main:app --reload --port=9595 +aws-lambda-fastapi/ +├── src/ # Main source code +│ ├── api/ # API layer - Routers and endpoints +│ │ ├── v1/ # API versioning +│ │ ├── routers.py # Main router registration +│ │ └── endpoints.py # Base endpoints (health check, etc.) +│ ├── core/ # Central configuration +│ │ ├── settings/ # Environment-specific configurations +│ │ ├── exceptions.py # Domain-specific exceptions +│ │ └── internal_codes.py # Domain internal codes +│ ├── shared/ # Shared utilities +│ │ ├── middlewares/ # Custom middlewares +│ │ ├── base_responses.py # Standardized responses +│ │ ├── base_internal_codes.py # Base internal codes +│ │ ├── base_exceptions.py # Base exceptions +│ │ └── utils_dates.py # Date utilities +│ ├── db/ # Database layer +│ │ ├── postgresql/ # PostgreSQL configuration +│ │ └── mongo/ # MongoDB configuration +│ ├── tests/ # Test suite +│ └── main.py # Application entry point +├── docker_images/ # Custom Docker images +│ └── testing/ # Testing configuration +├── .github/workflows/ # CI/CD pipelines +└── docs/ # Project documentation ``` -Run pre-commit +## 🔧 Main Components +### 1. **Standardized Response System** (`@/shared/base_responses.py`) + +This template implements a consistent response system using an **Envelope Response** pattern: + +```python +class EnvelopeResponse(BaseModel): + success: bool + message: str + data: dict[str, Any] | list | None = None + trace_id: str | None = None ``` -pre-commit run --all-files + +**Conventions:** +- All responses follow the same format +- Automatic Pydantic model serialization handling +- Trace_id inclusion for debugging +- Structured error responses with internal codes + +### 2. **Internal Code System** (`@/shared/base_internal_codes.py`) + +Standardized error code system for tracking and debugging: + +```python +class CommonInternalCode(InternalCodeBase): + UNKNOWN = 100, "Unknown error" + PYDANTIC_VALIDATIONS_REQUEST = 8001, "Failed Pydantic validations on request" +``` + +**Conventions:** +- Unique numeric codes for each error type +- Clear error description +- Extensible for domain-specific codes in `@/core/internal_codes.py` + +### 3. **Error Handling Middleware** (`@/shared/middlewares/`) + +#### CatcherExceptions +Main middleware that catches all exceptions: +- FastAPI HTTPException handling +- SQLAlchemy NoResultFound handling +- Custom exception handling +- Automatic conversion to standardized response format + +#### CatcherExceptionsPydantic +Specific middleware for Pydantic validation errors: +- Catches request body validation errors +- Consistent validation error formatting + +### 4. **Configuration System** (`@/core/settings/`) + +Environment-based configuration using Pydantic Settings: + ``` +settings/ +├── base.py # Base configuration +├── local.py # Local development +├── development.py # Development environment +├── staging.py # Staging environment +├── production.py # Production environment +└── testing.py # Testing configuration +``` + +**Conventions:** +- Typed environment variables +- Automatic configuration validation +- Environment-specific configuration +- Multi-database support + +### 5. **Multi-Database System** (`@/db/`) -Run mympi: +Template prepared for multiple database managers with **functional examples**: +#### PostgreSQL (`@/db/postgresql/`) ``` -mypy src +postgresql/ +├── base.py # Base class for SQLAlchemy models +├── connection.py # Connection configuration +├── models/ +│ └── public/ # 'public' schema +│ └── book.py # Example model +└── __init__.py # Main exports ``` +- **SQLAlchemy 2.0**: Modern ORM with modern syntax +- **Typed Models**: Full type hints and validation +- **Singleton Connection**: Reusable and efficient + +#### MongoDB (`@/db/mongo/`) +``` +mongo/ +├── base.py # Base class for documents +├── connection.py # Configured MongoDB client +├── models/ # Document models +└── __init__.py # Main exports +``` +- **PyMongo**: Official MongoDB driver +- **Pydantic Integration**: Models with automatic validation +- **Async Ready**: Prepared for asynchronous operations -Check docker deploy lambda +**Conventions:** +- Models organized by schemas/collections +- Singleton connection pattern +- Environment-specific configuration +- **Extensible**: Easy to add Redis, DynamoDB, etc. +### 6. **Modular API Structure** (`@/api/`) + +Versioning system and modular organization by domain: + +``` +api/ +├── v1/ # API Version 1 +│ └── books/ # Domain module (example) +│ ├── endpoints.py # Defines REST endpoints +│ ├── repositories.py # Database access +│ ├── schema.py # Pydantic models (DTOs) +│ └── services.py # Business logic +├── routers.py # Global router registration +└── endpoints.py # Base endpoints (health, index) ``` -curl -Xcurl -X POST "http://localhost:9100/2015-03-31/functions/function/invocations" \ + +**Module Conventions:** +- **endpoints.py**: Route definition and input validations +- **services.py**: Business logic, orchestration +- **repositories.py**: Data access, specific queries +- **schema.py**: DTOs (Data Transfer Objects) with Pydantic + +**Scalability**: You can add more modules (`users/`, `orders/`, etc.) following the same structure. + +### 7. **Testing System** (`@/tests/`) + +Complete automated testing suite: + +``` +tests/ +├── common/ # Common utilities for tests +├── utils/ # Testing utilities +├── v1/ # Version-specific tests +└── __init__.py # Database configuration for tests +``` + +**Conventions:** +- **Separate testing database**: Automatic configuration via `environment_testing` +- **Reusable fixtures**: Common utilities in `tests/common/` +- **Automated coverage**: Integrated in CI/CD with detailed reports +- **Tests organized by version**: Modular structure by API version +- **Automatic setup**: The `prepare_database()` script creates necessary schemas and tables each time the runner starts + +### 8. **Comprehensive Docker Stack** (`@/docker_images/`) + +The project comprehensively organizes Docker images for different purposes: + +#### **Testing Docker** (`@/docker_images/testing/`) +Specialized configuration for testing against **real databases** (not mocks): + +- **`Dockerfile.testing`**: Optimized image for running tests +- **`ci.env.sh`**: Script that configures environment variables for CI/CD +- **`entrypoint.sh`**: Orchestrates test execution and report generation + +**Database Selection in CI:** +```bash +# In ci.env.sh you can select which databases to spin up +export POSTGRESQL_URL="${GITHUB_DATABASE_POSTGRESQL:-$POSTGRESQL_URL}" +export MONGO_URL="${GITHUB_DATABASE_MONGODB:-$MONGO_URL}" +export REDIS_URL="${GITHUB_DATABASE_REDIS:-$REDIS_URL}" +``` + +Tests run against **real instances** of PostgreSQL, MongoDB, and Redis in the GitHub Actions runner. + +#### **Build and Deploy Docker** +- **`docker_images/build/`**: Optimized construction image +- **`docker_images/deploy/`**: Final image for AWS Lambda deployment + +## 🚀 Complete CI/CD Pipelines + +### **Main GitHub Actions Flow** + +The pipeline executes a complete and robust sequence: + +#### **1. 🔍 Linting (Pre-commit)** +```yaml +- Executes pre-commit for linting (configured in .pre-commit-config.yaml) +- Code formatting with Black +- Linting with Flake8 +- Type checking with MyPy +- Import and structure validation +``` + +#### **2. 🧪 Testing with Real Databases** +```yaml +services: + postgres: # PostgreSQL 13 on port 5432 + mongodb: # MongoDB 4.4 on port 27017 + redis: # Redis 6 on port 6379 +``` +- Spins up real database services +- Runs tests against real instances (not mocks) +- Generates detailed coverage reports + +#### **3. 📊 Coverage and Badges** +- **Generation**: `coverage run`, `coverage xml`, `coverage-badge` +- **Archival**: Uses `ronihdzz/git-archive-action@v3` to save artifacts +- **Storage**: Reports are stored in dedicated `artifacts` branch +- **Badges**: Dynamic coverage % badges are generated and displayed in README + +#### **4. 🏗️ Build and Deploy** +- Docker image construction optimized for Lambda +- Push to Amazon ECR (Elastic Container Registry) +- Automatic deploy to AWS Lambda +- Automatic versioning by environment (dev/staging/prod) + +### **Environment Configuration** + +```yaml +BRANCH_ENV_MAP: + "main": "prod" + "development": "dev" + "staging": "stg" +``` + +Each branch automatically maps to its corresponding environment. + +## 📦 Dependencies and Technologies + +### Core Dependencies +- **FastAPI**: Modern and fast web framework +- **Mangum**: Adapter for AWS Lambda +- **Pydantic**: Data validation and settings +- **SQLAlchemy**: ORM for relational databases +- **PyMongo**: MongoDB driver +- **Loguru**: Advanced logging + +### Development Dependencies +- **Pytest**: Testing framework +- **Coverage**: Code coverage +- **MyPy**: Type checking +- **Pre-commit**: Pre-commit hooks + +## 🛠️ Development Commands + +### Run the project locally +```bash +uvicorn main:app --reload --port=9595 +``` + +### Run tests +```bash +pytest src/tests/ -v --cov=src +``` + +### Run pre-commit hooks +```bash +pre-commit run --all-files +``` + +### Type checking +```bash +mypy src/ +``` + +### Testing with Docker +```bash +docker-compose -f docker-compose.yml up testing +``` + +## 🔍 Lambda Local Testing + +To test the Lambda function locally: + +```bash +curl -X POST "http://localhost:9100/2015-03-31/functions/function/invocations" \ -H "Content-Type: application/json" \ -d '{ "version": "2.0", @@ -47,3 +341,29 @@ curl -Xcurl -X POST "http://localhost:9100/2015-03-31/functions/function/invocat "isBase64Encoded": false }' ``` + +## 🎯 **Template Advantages** + +### **For Development** +- **Serverless Ready**: Optimized for AWS Lambda with Mangum +- **Multi-Database**: Native support for PostgreSQL, MongoDB, Redis +- **Type Safety**: Full TypeScript-like experience with MyPy +- **Error Handling**: Robust error handling system with internal codes + +### **For Testing** +- **Real Databases**: Tests against real databases, not mocks +- **Automated Setup**: Automatic creation of schemas and test data +- **Coverage Tracking**: Detailed reports with automatic badges +- **Docker Isolated**: Completely isolated testing environment + +### **For CI/CD** +- **Full Pipeline**: Linting → Testing → Coverage → Build → Deploy +- **Multi-Environment**: Automatic support for dev/staging/prod +- **Quality Gates**: Pre-commit hooks and automatic validations +- **Artifact Management**: Automatic storage of reports and badges + +### **For Production** +- **AWS Optimized**: Configured for AWS Lambda + API Gateway +- **Environment Config**: Typed configuration per environment +- **Monitoring Ready**: Structured logging with Loguru + Sentry +- **Scalable Architecture**: Modular structure easy to scale