Skip to content

eincioch/SimpleAzureFunction

Repository files navigation

Simple Azure Function - API de Productos

.NET Azure Functions PostgreSQL

API REST serverless construida con Azure Functions v4 y .NET 8 que implementa un CRUD completo para la gestión de productos, utilizando Entity Framework Core con PostgreSQL como base de datos.


📋 Tabla de Contenidos


🏗️ Arquitectura

┌─────────────────────────────────────────────────────────────────┐
│                        Azure Functions Host                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐    ┌─────────────────┐    ┌────────────────┐  │
│  │   HTTP       │───▶│   Middleware    │───▶│   Functions    │  │
│  │   Trigger    │    │   (Logging)     │    │   (Endpoints)  │  │
│  └──────────────┘    └─────────────────┘    └───────┬────────┘  │
│                                                      │           │
│                                              ┌───────▼────────┐  │
│                                              │  IProductService│  │
│                                              │    (Service)   │  │
│                                              └───────┬────────┘  │
│                                                      │           │
│                                              ┌───────▼────────┐  │
│                                              │  DbContext     │  │
│                                              │  (EF Core)     │  │
│                                              └───────┬────────┘  │
│                                                      │           │
└──────────────────────────────────────────────────────┼───────────┘
                                                       │
                                               ┌───────▼────────┐
                                               │   PostgreSQL   │
                                               │   Database     │
                                               └────────────────┘

🛠️ Tecnologías

Tecnología Versión Descripción
.NET 8.0 Framework principal
Azure Functions v4 Plataforma serverless
Entity Framework Core 8.0.10 ORM para acceso a datos
PostgreSQL (Npgsql) 8.0.10 Base de datos relacional
Ardalis.Result 10.1.0 Patrón Result para manejo de respuestas
OpenAPI 1.5.1 Documentación de API
Application Insights 2.22.0 Monitoreo y telemetría

📁 Estructura del Proyecto

FunctionApp/
├── Database/
│   ├── Configurations/
│   │   └── ProductConfiguration.cs     # Configuración de entidad EF Core
│   ├── Constants/
│   │   └── TableNames.cs               # Constantes de nombres de tablas
│   └── ApplicationDbContext.cs         # Contexto de base de datos
├── Entities/
│   └── Product.cs                      # Entidad de dominio
├── Extensions/
│   ├── HostExtensions.cs               # Extensiones para migraciones
│   └── HttpRequestExtensions.cs        # Extensiones para HttpRequest
├── Functions/
│   ├── CreateProduct.cs                # POST /api/products
│   ├── GetAllProducts.cs               # GET /api/products
│   ├── GetProductById.cs               # GET /api/products/{id}
│   ├── UpdateProduct.cs                # PUT /api/products/{id}
│   └── DeleteProduct.cs                # DELETE /api/products/{id}
├── Middlewares/
│   └── CustomMiddleware.cs             # Middleware de logging
├── Migrations/
│   └── ...                             # Migraciones de EF Core
├── Services/
│   ├── Abstraction/
│   │   └── IProductService.cs          # Interfaz del servicio
│   └── ProductService.cs               # Implementación del servicio
├── Program.cs                          # Punto de entrada
├── host.json                           # Configuración del host
└── local.settings.json                 # Configuración local

✅ Prerrequisitos


⚙️ Configuración

1. Configurar la base de datos

Crea un archivo local.settings.json en la raíz del proyecto FunctionApp:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "Postgres": "Host=localhost;Database=ProductsDb;Username=postgres;Password=tu_password"
  }
}

2. Aplicar migraciones

Las migraciones se aplican automáticamente al iniciar la aplicación mediante el método ApplyMigrations() en Program.cs.

Para crear nuevas migraciones manualmente:

cd FunctionApp
dotnet ef migrations add NombreMigracion

🚀 Ejecución Local

Usando Azure Functions Core Tools

cd FunctionApp
func start

Usando Visual Studio

  1. Abrir la solución
  2. Establecer FunctionApp como proyecto de inicio
  3. Presionar F5

Usando Docker

docker build -t simple-azure-function .
docker run -p 7071:80 simple-azure-function

La API estará disponible en: http://localhost:7071/api/


📡 Endpoints API

Método Ruta Descripción Request Body Response
GET /api/products Obtener todos los productos - List<Product>
GET /api/products/{id} Obtener producto por ID - Product o 404
POST /api/products Crear nuevo producto CreateProductRequest Guid
PUT /api/products/{id} Actualizar producto UpdateProductRequest 204 o 404
DELETE /api/products/{id} Eliminar producto - 204 o 404

Modelos de Request

CreateProductRequest

{
  "name": "Producto Ejemplo",
  "description": "Descripción del producto",
  "price": 99.99
}

UpdateProductRequest

{
  "description": "Nueva descripción",
  "price": 149.99
}

Modelo Product

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Producto Ejemplo",
  "description": "Descripción del producto",
  "price": 99.99
}

🔄 Flujos de la Aplicación

Flujo 1: Crear Producto (POST /api/products)

┌──────────┐     ┌──────────────┐     ┌────────────────┐     ┌─────────────┐     ┌────────────┐
│  Cliente │────▶│ HTTP Trigger │────▶│ CustomMiddleware│────▶│CreateProduct│────▶│ProductService│
└──────────┘     └──────────────┘     └────────────────┘     └──────┬──────┘     └──────┬─────┘
                                                                    │                   │
                                                                    │   Create()        │
                                                                    │◀──────────────────│
                                                                    │                   │
                                                              ┌─────▼─────┐       ┌─────▼─────┐
                                                              │  Return   │       │ DbContext │
                                                              │   Guid    │       │  SaveAsync│
                                                              └───────────┘       └─────┬─────┘
                                                                                        │
                                                                                  ┌─────▼─────┐
                                                                                  │PostgreSQL │
                                                                                  │  INSERT   │
                                                                                  └───────────┘

Pasos detallados:

  1. El cliente envía una petición POST con el body JSON
  2. El HTTP Trigger activa la función CreateProduct
  3. El CustomMiddleware registra la entrada en logs
  4. Se deserializa el request usando HttpRequestExtensions.GetBody<T>()
  5. Se llama a IProductService.Create() con los datos
  6. ProductService crea una nueva entidad Product
  7. EF Core persiste el producto en PostgreSQL
  8. Se retorna el GUID del producto creado

Flujo 2: Obtener Todos los Productos (GET /api/products)

┌──────────┐     ┌──────────────┐     ┌────────────────┐     ┌───────────────┐
│  Cliente │────▶│ HTTP Trigger │────▶│ CustomMiddleware│────▶│GetAllProducts │
└──────────┘     └──────────────┘     └────────────────┘     └───────┬───────┘
                                                                     │
                                                               ┌─────▼─────┐
                                                               │ProductService│
                                                               │  GetAll() │
                                                               └─────┬─────┘
                                                                     │
                                                               ┌─────▼─────┐
                                                               │ DbContext │
                                                               │ToListAsync│
                                                               └─────┬─────┘
                                                                     │
                                                               ┌─────▼─────┐
                                                               │PostgreSQL │
                                                               │  SELECT * │
                                                               └─────┬─────┘
                                                                     │
                                                               ┌─────▼─────┐
                                                               │Return JSON│
                                                               │List<Product>│
                                                               └───────────┘

Pasos detallados:

  1. El cliente envía una petición GET
  2. Se ejecuta el middleware de logging
  3. ProductService.GetAll() ejecuta una consulta a la base de datos
  4. EF Core traduce la consulta a SQL SELECT
  5. Se retorna la lista de productos como JSON

Flujo 3: Obtener Producto por ID (GET /api/products/{id})

┌──────────┐     ┌──────────────┐     ┌───────────────┐     ┌─────────────┐
│  Cliente │────▶│ HTTP Trigger │────▶│GetProductById │────▶│ProductService│
└──────────┘     └──────────────┘     └───────────────┘     └──────┬──────┘
                                                                   │
                                                             ┌─────▼─────┐
                                                             │ GetById() │
                                                             │Result<T>  │
                                                             └─────┬─────┘
                                                                   │
                                              ┌────────────────────┼────────────────────┐
                                              │                    │                    │
                                        ┌─────▼─────┐        ┌─────▼─────┐        ┌─────▼─────┐
                                        │  Existe   │        │  Producto │        │No Existe │
                                        │   ✓       │        │   Found   │        │   ✗      │
                                        └─────┬─────┘        └───────────┘        └─────┬─────┘
                                              │                                         │
                                        ┌─────▼─────┐                             ┌─────▼─────┐
                                        │ Return    │                             │ Return    │
                                        │ 200 + JSON│                             │ 404       │
                                        └───────────┘                             └───────────┘

Pasos detallados:

  1. El cliente envía GET con el GUID en la ruta
  2. ProductService.GetById() busca el producto
  3. Se usa el patrón Result<T> de Ardalis.Result
  4. Si existe → retorna 200 OK con el producto
  5. Si no existe → retorna 404 Not Found

Flujo 4: Actualizar Producto (PUT /api/products/{id})

┌──────────┐     ┌──────────────┐     ┌─────────────┐     ┌─────────────┐
│  Cliente │────▶│ HTTP Trigger │────▶│UpdateProduct│────▶│ProductService│
│  PUT     │     │              │     │             │     │  Update()   │
└──────────┘     └──────────────┘     └─────────────┘     └──────┬──────┘
                                                                 │
                                                           ┌─────▼─────┐
                                                           │Find Product│
                                                           │by ID       │
                                                           └─────┬─────┘
                                                                 │
                                          ┌──────────────────────┼──────────────────────┐
                                          │                      │                      │
                                    ┌─────▼─────┐          ┌─────▼─────┐          ┌─────▼─────┐
                                    │  Existe   │          │  Update   │          │No Existe │
                                    │   ✓       │─────────▶│Entity     │          │   ✗      │
                                    └───────────┘          └─────┬─────┘          └─────┬─────┘
                                                                 │                      │
                                                           ┌─────▼─────┐          ┌─────▼─────┐
                                                           │SaveChanges│          │ Return    │
                                                           │Return 204 │          │ 404       │
                                                           └───────────┘          └───────────┘

Pasos detallados:

  1. El cliente envía PUT con ID en ruta y datos en body
  2. Se deserializa UpdateProductRequest
  3. ProductService.Update() busca el producto
  4. Si existe → actualiza Description y Price
  5. EF Core persiste los cambios
  6. Retorna 204 No Content o 404 Not Found

Flujo 5: Eliminar Producto (DELETE /api/products/{id})

┌──────────┐     ┌──────────────┐     ┌─────────────┐     ┌─────────────┐
│  Cliente │────▶│ HTTP Trigger │────▶│DeleteProduct│────▶│ProductService│
│  DELETE  │     │              │     │             │     │  Delete()   │
└──────────┘     └──────────────┘     └─────────────┘     └──────┬──────┘
                                                                 │
                                                           ┌─────▼─────┐
                                                           │Find Product│
                                                           └─────┬─────┘
                                                                 │
                                          ┌──────────────────────┼──────────────────────┐
                                          │                      │                      │
                                    ┌─────▼─────┐          ┌─────▼─────┐          ┌─────▼─────┐
                                    │  Existe   │          │  Remove   │          │No Existe │
                                    │   ✓       │─────────▶│  Entity   │          │   ✗      │
                                    └───────────┘          └─────┬─────┘          └─────┬─────┘
                                                                 │                      │
                                                           ┌─────▼─────┐          ┌─────▼─────┐
                                                           │SaveChanges│          │ Return    │
                                                           │Return 204 │          │ 404       │
                                                           └───────────┘          └───────────┘

Flujo de Inicialización de la Aplicación

┌─────────────────────────────────────────────────────────────────────────────┐
│                           Program.cs - Startup                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  1. HostBuilder                                                              │
│     │                                                                        │
│     ├──▶ ConfigureFunctionsWebApplication()                                  │
│     │    └──▶ UseMiddleware<CustomMiddleware>()                              │
│     │                                                                        │
│     ├──▶ ConfigureServices()                                                 │
│     │    ├──▶ AddDbContext<ApplicationDbContext>()                           │
│     │    │    └──▶ UseNpgsql(connectionString)                               │
│     │    │                                                                   │
│     │    └──▶ AddScoped<IProductService, ProductService>()                   │
│     │                                                                        │
│     └──▶ ConfigureOpenApi()                                                  │
│                                                                              │
│  2. Build()                                                                  │
│     │                                                                        │
│     └──▶ host.ApplyMigrations()                                              │
│          └──▶ Database.Migrate()                                             │
│                                                                              │
│  3. Run()                                                                    │
│     └──▶ Aplicación lista para recibir requests                              │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

📖 Documentación OpenAPI

La API incluye documentación OpenAPI/Swagger automática. Después de iniciar la aplicación, accede a:

  • Swagger UI: http://localhost:7071/api/swagger/ui
  • OpenAPI JSON: http://localhost:7071/api/openapi/v3.json

Cada endpoint está documentado con:

  • Operación y tags
  • Parámetros de ruta
  • Request body schemas
  • Response schemas y códigos de estado

🐳 Docker

El proyecto está configurado para ejecutarse en contenedores Docker con soporte para Linux.

Dockerfile

El proyecto incluye configuración para Docker con:

  • Target OS: Linux
  • Mount directory: /home/site/wwwroot

Variables de entorno necesarias

Postgres=Host=db;Database=ProductsDb;Username=postgres;Password=password
AzureWebJobsStorage=UseDevelopmentStorage=true
FUNCTIONS_WORKER_RUNTIME=dotnet-isolated

📝 Licencia

Este proyecto está bajo la licencia MIT.


👤 Autor

eincioch

Repositorio: SimpleAzureFunction

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published