Azure Functions vs API tradicional: cuándo usar cada una
Azure Functions parece más barato que una API en App Service hasta que aparecen arranques en frío, límites de tiempo y lógica repartida. Cómo decidir.
Las Azure Functions se presentan con una promesa atractiva: escribes solo la función, Azure se encarga de escalarla y pagas únicamente por las ejecuciones. Para muchos casos es exactamente así. Para otros, la promesa se vuelve una API partida en cuarenta funciones, con arranques en frío en el primer clic de la mañana y una factura difícil de predecir.
La pregunta útil no es si Functions es mejor que una API tradicional, sino qué forma tiene la carga de trabajo.
Dónde Functions encaja muy bien
Trabajo disparado por eventos
Es su mejor caso. Algo ocurre y hay que reaccionar:
public class OrderCreatedFunction(ILogger<OrderCreatedFunction> logger, IInvoiceService invoices)
{
[Function(nameof(OrderCreatedFunction))]
public async Task Run(
[ServiceBusTrigger("orders-created", Connection = "ServiceBus")] OrderCreatedMessage message,
CancellationToken ct)
{
logger.LogInformation("Generando factura del pedido {OrderId}", message.OrderId);
await invoices.GenerateAsync(message.OrderId, ct);
}
}
Un mensaje en una cola, un archivo subido a Blob Storage, un cambio en Cosmos DB. La función escala con la cantidad de eventos y no consume nada cuando no hay.
Tareas programadas
[Function("NightlyCleanup")]
public async Task Run([TimerTrigger("0 0 3 * * *")] TimerInfo timer, CancellationToken ct) { ... }
Un proceso que corre a las 3 a. m. no necesita un servidor encendido las otras 23 horas.
Cargas muy variables o esporádicas
Un webhook que recibe mil llamadas en un minuto y ninguna el resto del día. Un proceso que solo corre a fin de mes.
Dónde una API tradicional suele ser mejor
APIs con tráfico constante y latencia importante
En el plan de consumo, una función inactiva se descarga. La siguiente petición paga el arranque en frío: cargar el proceso, el runtime de .NET y tus dependencias. Puede ser desde unos cientos de milisegundos hasta varios segundos. Para un webhook da igual; para la API que usa tu aplicación móvil, el usuario lo nota.
El plan Flex Consumption y el plan Premium permiten instancias siempre listas que reducen ese problema, pero tienen un costo fijo, y ahí la diferencia de precio con un App Service se reduce mucho.
Muchos endpoints que comparten lógica y estado
Una API con 60 endpoints, autenticación, validación, middleware y versionado es exactamente para lo que existe ASP.NET Core. Llevarla a Functions significa repartir esa lógica en funciones HTTP y reconstruir lo que el pipeline de ASP.NET Core ya da (aunque el modelo aislado de Functions admite parte de la integración con ASP.NET Core).
Procesos largos
En el plan de consumo, una ejecución tiene un tiempo máximo (por defecto 5 minutos, configurable hasta 10). Para procesos más largos hay que usar otros planes, partir el trabajo o usar Durable Functions, que agregan su propio modelo de programación.
Conexiones a la base de datos
Una API mantiene un pool de conexiones estable. Cuando Functions escala a decenas de instancias por un pico de mensajes, cada una abre sus propias conexiones y la base de datos puede quedarse sin ellas. Hay que limitar la concurrencia (por ejemplo, maxConcurrentCalls en el disparador de Service Bus o un máximo de instancias) para proteger a los sistemas que están detrás.
El modelo actual: worker aislado
Si empiezas hoy con .NET, el modelo es el worker aislado (isolated worker): la función corre en su propio proceso, con su propia versión de .NET, inyección de dependencias y middleware. El modelo in-process deja de tener soporte en noviembre de 2026, así que los proyectos que lo usan deberían estar migrando.
var builder = FunctionsApplication.CreateBuilder(args);
builder.Services.AddScoped<IInvoiceService, InvoiceService>();
builder.Services.AddDbContext<AppDbContext>(o => o.UseSqlServer(builder.Configuration.GetConnectionString("Sql")));
builder.Build().Run();
Una combinación que funciona bien
En muchas arquitecturas, la respuesta no es una u otra sino las dos:
Aplicación móvil / web
│
▼
API en App Service (ASP.NET Core) ──► publica "pedido creado" en Service Bus
│
▼
Azure Functions: factura, correo, inventario
La API atiende a los usuarios con latencia baja y estable. Las funciones hacen el trabajo asíncrono, escalan con los eventos y no hacen esperar al usuario.
El trade-off
| Azure Functions (consumo) | API en App Service | |
|---|---|---|
| Costo con poco tráfico | Casi cero | Costo fijo del plan |
| Costo con tráfico constante alto | Puede superar al fijo | Predecible |
| Latencia del primer request | Arranque en frío | Estable |
| Escalado | Automático por eventos | Reglas de escalado |
| Procesos largos | Limitados | Sin límite propio |
| Estructura para APIs grandes | Fragmentada | Natural |
Cuándo no usar Functions
- Como reemplazo directo de una API con tráfico constante y requisitos de latencia.
- Cuando la lógica de negocio queda repartida en decenas de funciones que se llaman entre sí por HTTP.
- Cuando no puedes limitar cuánto escala y los sistemas detrás (base de datos, APIs de terceros) tienen capacidad limitada.
Conclusión
Functions es excelente para reaccionar a eventos, ejecutar tareas programadas y absorber cargas esporádicas. Una API tradicional sigue siendo la mejor opción para atender usuarios con tráfico constante. La mayoría de sistemas que funcionan bien en Azure usan las dos, cada una para lo suyo. Y cuando la API y las funciones comparten una base de datos, revisa cómo se comportan los niveles de aislamiento bajo esa concurrencia.
Lleva tu aplicación a la nube con criterio
Diseñamos arquitecturas en Azure que crecen con tu negocio sin disparar los costos de infraestructura.
Ver soluciones escalables