Arquitectura Serverless en 2025: Por Qué Tu Próxima API Debería Ser Serverless
Hola HaWkers, ¿todavía mantienes servidores corriendo 24/7 esperando requisiciones? ¿Pagas por capacidad que no usas 90% del tiempo? ¿Pasas madrugadas configurando auto-scaling?
Serverless cambió el juego. En 2025, la pregunta no es más "¿debo usar serverless?", sino "¿por qué todavía no estoy usando?". Voy a mostrarte exactamente cómo funciona, cuándo usar, y código real de producción.
¿Qué Es Serverless (De Verdad)?
Serverless NO significa "sin servidores". Significa que tú no gestionas servidores.
Modelo tradicional:
- Tú provisionas EC2/VPS
- Instalas Node.js, PM2, nginx
- Configuras load balancer, auto-scaling
- Monitoreas uso de CPU/RAM
- Pagas 24/7, incluso sin tráfico
Modelo serverless:
- Tú escribes función
- Deploy con un comando
- Plataforma gestiona todo
- Escala automáticamente (0 a millones de requisiciones)
- Pagas apenas por ejecución real
Principales Plataformas en 2025
1. AWS Lambda (El Gigante)
- Mayor ecosistema (integración con 200+ servicios AWS)
- Soporta Node.js, Python, Go, Java, .NET, Rust
- Cold start: ~100-200ms (mejoró mucho)
- Pricing: $0.20 por 1M requisiciones + compute time
2. Vercel Functions (El Dev-Friendly)
- Deploy integrado con Git
- Edge network global
- Perfecto para Next.js/React
- Cold start: ~50ms
- Pricing: 100k invocaciones gratis, después $0.40/1M
3. Cloudflare Workers (El Más Rápido)
- Edge computing (corre en 300+ ciudades)
- Cold start: ~0ms (siempre "caliente")
- V8 isolates (no containers)
- Pricing: 100k/día gratis, después $0.50/1M
4. Netlify Functions (El Simple)
- Integración perfecta con JAMstack
- Deploy automático
- Good for startups
- Pricing: 125k/mes gratis
Tu Primera Function: Hello World Real
AWS Lambda + API Gateway:
// handler.js
exports.handler = async (event) => {
const { name = 'World' } = event.queryStringParameters || {};
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString(),
requestId: event.requestContext.requestId
})
};
};Deploy con Serverless Framework:
# serverless.yml
service: hello-world-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
functions:
hello:
handler: handler.handler
events:
- httpApi:
path: /hello
method: get
# Deploy con un comando
# serverless deployResultado: API corriendo en https://xxxxxxx.execute-api.us-east-1.amazonaws.com/hello?name=Jeff

Caso Real: API de E-commerce
Vamos a construir API completa de productos con CRUD, validación y cache.
// api/products/index.js
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
export const handler = async (event) => {
const { httpMethod, pathParameters, body } = event;
try {
switch (httpMethod) {
case 'GET':
return pathParameters?.id
? await getProduct(pathParameters.id)
: await listProducts();
case 'POST':
return await createProduct(JSON.parse(body));
case 'PUT':
return await updateProduct(pathParameters.id, JSON.parse(body));
case 'DELETE':
return await deleteProduct(pathParameters.id);
default:
return response(405, { error: 'Method not allowed' });
}
} catch (error) {
console.error('Error:', error);
return response(500, { error: error.message });
}
};
// Helper: Respuesta estandarizada
function response(statusCode, data) {
return {
statusCode,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(data)
};
}
// GET /products
async function listProducts() {
const command = new ScanCommand({
TableName: process.env.PRODUCTS_TABLE
});
const result = await docClient.send(command);
return response(200, {
products: result.Items,
count: result.Count
});
}
// GET /products/:id
async function getProduct(id) {
const { GetCommand } = await import('@aws-sdk/lib-dynamodb');
const command = new GetCommand({
TableName: process.env.PRODUCTS_TABLE,
Key: { id }
});
const result = await docClient.send(command);
if (!result.Item) {
return response(404, { error: 'Product not found' });
}
return response(200, result.Item);
}
// POST /products
async function createProduct(data) {
const { PutCommand } = await import('@aws-sdk/lib-dynamodb');
// Validación
const errors = validateProduct(data);
if (errors.length > 0) {
return response(400, { errors });
}
const product = {
id: crypto.randomUUID(),
...data,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const command = new PutCommand({
TableName: process.env.PRODUCTS_TABLE,
Item: product
});
await docClient.send(command);
return response(201, product);
}
// Validación de producto
function validateProduct(data, isUpdate = false) {
const errors = [];
if (!isUpdate && !data.name) {
errors.push('Name is required');
}
if (data.name && data.name.length < 3) {
errors.push('Name must be at least 3 characters');
}
if (!isUpdate && data.price === undefined) {
errors.push('Price is required');
}
if (data.price !== undefined && (data.price < 0 || isNaN(data.price))) {
errors.push('Price must be a positive number');
}
return errors;
}
Optimizaciones Esenciales
1. Reducir Cold Starts:
// ❌ Malo: Import dentro de la función
export const handler = async () => {
const AWS = require('aws-sdk'); // Import a cada ejecución
const db = new AWS.DynamoDB.DocumentClient();
// ...
};
// ✅ Bueno: Import en el tope (reutilizado)
const { DynamoDBDocumentClient } = require('@aws-sdk/lib-dynamodb');
const docClient = DynamoDBDocumentClient.from(new DynamoDBClient({}));
export const handler = async () => {
// docClient ya está instanciado
};
// ✅ Aún mejor: Lazy loading
let docClient;
function getDocClient() {
if (!docClient) {
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient } = require('@aws-sdk/lib-dynamodb');
docClient = DynamoDBDocumentClient.from(new DynamoDBClient({}));
}
return docClient;
}2. Provisioned Concurrency (Para APIs críticas):
# serverless.yml
functions:
api:
handler: handler.handler
provisionedConcurrency: 2 # Siempre 2 instancias "calientes"
reservedConcurrency: 100 # Limita concurrencia máxima3. Caching Inteligente:
// Cache en memoria (persiste entre invocaciones de la misma instancia)
let cachedProducts = null;
let cacheExpiry = 0;
async function getProductsCached() {
const now = Date.now();
if (cachedProducts && now < cacheExpiry) {
console.log('Cache hit!');
return cachedProducts;
}
console.log('Cache miss, fetching...');
const products = await fetchProductsFromDB();
cachedProducts = products;
cacheExpiry = now + (5 * 60 * 1000); // Cache por 5 minutos
return products;
}Costos Reales: Comparación
Escenario: API con 1M requisiciones/mes, 200ms promedio
| Solución | Costo/mes | Detalles |
|---|---|---|
| EC2 t3.small (24/7) | $17 | + $5 Load Balancer = $22 |
| AWS Lambda | $0.40 | $0.20 req + $0.20 compute |
| Vercel Functions | $0.40 | Después de 100k gratis |
| Cloudflare Workers | $5 | Plan pago ($5/mes) |
Ventaja serverless: Ahorra 80-90% en tráfico bajo/medio.
Cuando serverless se vuelve caro:
- Tráfico constante alto (>10M req/mes)
- Funciones de larga duración (>15min)
Cuándo NO Usar Serverless
Evita para:
- WebSockets persistentes (usa EC2/ECS)
- Procesamiento pesado (ML training, video encoding)
- Latencia crítica (<10ms consistente)
- Estado persistente en memoria (caches grandes)
Alternativa híbrida:
// API serverless + Worker tradicional
// Lambda para endpoints
export const api = async (event) => {
// Procesa requisición leve
// Para tareas pesadas, envía para cola
await sqs.sendMessage({
queueUrl: WORKER_QUEUE,
body: JSON.stringify({ task: 'heavy-compute' })
});
};
// EC2 worker procesa cola
// Corre 24/7, optimizado para tareas pesadasEl Futuro del Serverless
Tendencias 2025:
- Edge computing (Cloudflare Workers, Vercel Edge)
- Serverless containers (AWS Fargate, Cloud Run)
- Streaming responses (datos progresivos)
- IA integrada (embeddings, summarization)
Para entender mejor patrones asíncronos esenciales en serverless, confiere Descubriendo el Poder del Async/Await en JavaScript.
¡Vamos a por ello! 🦅
Domina JavaScript de Verdad
El conocimiento que adquiriste en este artículo es solo el comienzo. Hay técnicas, patrones y prácticas que transforman desarrolladores principiantes en profesionales solicitados.
Invierte en Tu Futuro
Preparé un material completo para que domines JavaScript:
Formas de pago:
- $9.90 USD (pago único)

