Volver al blog

Serverless y Edge Functions: Por Que 75% de las Nuevas Aplicaciones Usan Esta Arquitectura

Hola HaWkers, la forma como deployamos aplicaciones cambio drasticamente en los ultimos anos. Serverless ya no es novedad, pero Edge Functions agregaron una nueva dimension al colocar tu codigo geograficamente cerca de los usuarios. Hoy, 75% de las nuevas aplicaciones web ya nacen con algun componente serverless o edge.

Pero cuando usar cada enfoque? Cuales son los trade-offs reales? Y como JavaScript y Node.js encajan en este escenario?

El Panorama Actual

En 2025, las principales plataformas expandieron significativamente sus ofertas de edge y serverless:

Principales Players

Cloudflare Workers:

  • 320+ puntos de presencia global
  • Startup en < 5ms (cold start)
  • Soporte a WebSockets, Durable Objects
  • Precio agresivo (100k requests gratis/dia)

Vercel Edge Functions:

  • Integrado con Next.js, Nuxt, SvelteKit
  • Deploy automatico via Git
  • Edge Middleware para routing inteligente
  • Cache edge granular

AWS Lambda@Edge / CloudFront Functions:

  • Integrado al ecosistema AWS
  • Mayor numero de regiones
  • Triggers de CDN eventos
  • Escala masiva comprobada

Deno Deploy:

  • TypeScript nativo
  • Deploy instantaneo
  • Latencia ultra baja
  • Framework-agnostico

Numeros de Adopcion

Estadisticas de 2025:

  • 75% de las nuevas aplicaciones web usan serverless
  • 45% ya usan edge functions
  • Reduccion media de 60% en latencia con edge
  • Costo 40-70% menor que servidores tradicionales

Entendiendo las Diferencias

Serverless y Edge son conceptos relacionados pero distintos.

Serverless Tradicional (Lambda, Cloud Functions)

Tu codigo corre en un data center, pero no gestionas servidores:

// AWS Lambda - Handler clasico
export const handler = async (event) => {
  const { body } = event;
  const data = JSON.parse(body);

  // Procesa los datos
  const result = await processData(data);

  // Guarda en DynamoDB
  await dynamoDB.put({
    TableName: 'orders',
    Item: result
  }).promise();

  return {
    statusCode: 200,
    body: JSON.stringify({ success: true, id: result.id })
  };
};

Caracteristicas:

  • Cold starts pueden llegar a 1-5 segundos
  • Acceso a todo el ecosistema cloud (databases, queues, etc.)
  • Sin limites estrictos de memoria/tiempo (configurable)
  • Ideal para procesamiento pesado

Edge Functions

Tu codigo corre en puntos de presencia cerca del usuario:

// Cloudflare Workers - Edge Function
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Logica de routing en el edge
    if (url.pathname.startsWith('/api/')) {
      return handleApi(request, env);
    }

    // Personalizacion por geolocalizacion
    const country = request.cf?.country || 'US';
    const content = await getLocalizedContent(country, env);

    return new Response(content, {
      headers: { 'Content-Type': 'text/html' }
    });
  }
};

async function getLocalizedContent(country: string, env: Env) {
  // Busqueda del KV Storage en el edge
  const cached = await env.CONTENT_KV.get(`home-${country}`);
  if (cached) return cached;

  // Fallback
  return env.CONTENT_KV.get('home-default');
}

Caracteristicas:

  • Cold starts en < 50ms (generalmente < 5ms)
  • Limitaciones de runtime (CPU time, memoria)
  • APIs limitadas (sin filesystem, algunas Node APIs)
  • Ideal para logica leve y personalizacion

Casos de Uso Practicos

Cuando Usar Edge Functions

A/B Testing y Feature Flags:

// Vercel Edge Middleware
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // Determina bucket de A/B test en el edge
  const bucket = request.cookies.get('ab-bucket')?.value
    || (Math.random() < 0.5 ? 'A' : 'B');

  const response = NextResponse.next();

  // Seta cookie si nuevo usuario
  if (!request.cookies.get('ab-bucket')) {
    response.cookies.set('ab-bucket', bucket, {
      maxAge: 60 * 60 * 24 * 30 // 30 dias
    });
  }

  // Rewrite para version correcta
  if (bucket === 'B' && request.nextUrl.pathname === '/checkout') {
    return NextResponse.rewrite(new URL('/checkout-new', request.url));
  }

  return response;
}

export const config = {
  matcher: ['/checkout', '/product/:path*']
};

Autenticacion y Proteccion:

// Validacion de JWT en el edge
import { jwtVerify } from 'jose';

export default {
  async fetch(request: Request, env: Env) {
    const token = request.headers.get('Authorization')?.replace('Bearer ', '');

    if (!token) {
      return new Response('Unauthorized', { status: 401 });
    }

    try {
      const secret = new TextEncoder().encode(env.JWT_SECRET);
      const { payload } = await jwtVerify(token, secret);

      // Agrega user info al request
      const newHeaders = new Headers(request.headers);
      newHeaders.set('X-User-Id', payload.sub as string);

      return fetch(request, { headers: newHeaders });
    } catch {
      return new Response('Invalid token', { status: 401 });
    }
  }
};

Cuando Usar Serverless Tradicional

Procesamiento Pesado:

// AWS Lambda para procesamiento de imagen
import sharp from 'sharp';
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';

export const handler = async (event) => {
  const { bucket, key } = event;

  // Busca imagen original
  const s3 = new S3Client({});
  const original = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));

  // Procesa con Sharp (no disponible en el edge)
  const processed = await sharp(await original.Body.transformToByteArray())
    .resize(800, 600, { fit: 'inside' })
    .webp({ quality: 80 })
    .toBuffer();

  // Guarda version optimizada
  await s3.send(new PutObjectCommand({
    Bucket: bucket,
    Key: key.replace(/\.\w+$/, '.webp'),
    Body: processed,
    ContentType: 'image/webp'
  }));

  return { processed: true };
};

Integracion con Databases:

// Lambda con conexion a banco
import { Pool } from 'pg';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 10
});

export const handler = async (event) => {
  const { userId, action, data } = JSON.parse(event.body);

  const client = await pool.connect();
  try {
    await client.query('BEGIN');

    // Transaccion compleja
    const result = await client.query(
      'INSERT INTO orders (user_id, data) VALUES ($1, $2) RETURNING id',
      [userId, data]
    );

    await client.query(
      'UPDATE users SET last_order = NOW() WHERE id = $1',
      [userId]
    );

    await client.query('COMMIT');

    return {
      statusCode: 200,
      body: JSON.stringify({ orderId: result.rows[0].id })
    };
  } catch (e) {
    await client.query('ROLLBACK');
    throw e;
  } finally {
    client.release();
  }
};

Arquitectura Hibrida

El enfoque mas comun en 2025 es combinar edge y serverless:

Patron Recomendado

Usuario
   |
   v
[Edge Function] ─────────────────┐
   │                             │
   │ - Auth validation           │
   │ - A/B testing               │
   │ - Geo routing               │
   │ - Cache check               │
   │                             │
   v                             v
[CDN Cache] <──── o ────> [Origin/Lambda]
   │                             │
   │ - Static assets             │ - Business logic
   │ - Cached responses          │ - Database ops
   │                             │ - Heavy processing
   v                             v
[Usuario]                  [Database/Storage]

Ejemplo con Next.js

// middleware.ts (Edge)
export function middleware(request: NextRequest) {
  // Logica leve en el edge
  const country = request.geo?.country || 'US';
  const response = NextResponse.next();
  response.headers.set('X-Country', country);
  return response;
}

// app/api/orders/route.ts (Serverless)
export async function POST(request: Request) {
  // Logica pesada en serverless
  const data = await request.json();

  const order = await prisma.order.create({
    data: {
      ...data,
      country: request.headers.get('X-Country')
    }
  });

  return Response.json(order);
}

Trade-offs y Consideraciones

Limitaciones del Edge

Restricciones comunes:

  • CPU time limitado (generalmente 50-100ms)
  • Memoria limitada (128MB tipico)
  • Sin acceso a filesystem
  • Subset de APIs Node.js
  • Conexiones de banco mas complejas

Cuando NO Usar Edge

Evita edge para:

  • Queries complejas de banco
  • Procesamiento de archivos grandes
  • Operaciones que llevan > 100ms
  • Dependencias Node.js pesadas (sharp, puppeteer)

Costos

Enfoque Costo Tipico Cold Start Latencia
Edge Function $0.50/millon req < 5ms 10-50ms
Lambda $0.20/millon req 100-5000ms 50-200ms
Container $50-200/mes 0ms 20-100ms

Conclusion

Serverless y Edge Functions no son enfoques exclusivos, sino complementarios. Edge es ideal para logica leve, personalizacion y reduccion de latencia. Serverless tradicional continua siendo la mejor opcion para procesamiento pesado e integracion con databases.

En 2025, la mayoria de las aplicaciones bien arquitectadas usa ambos: edge para la primera capa de procesamiento y serverless para operaciones mas complejas.

Si quieres entender mas sobre arquitecturas modernas de JavaScript, recomiendo conferir el articulo sobre Async/Await en JavaScript donde exploramos patrones que funcionan bien en estas arquitecturas.

Vamos para arriba! 🦅

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios