Volver al blog

Serverless y Edge Computing: Arquitectura de Baja Latencia que Domina 2025

Hola HaWkers, ¿recuerdas cuando deployar una aplicación significaba provisionar servidores, configurar balanceadores de carga, gestionar escalabilidad manualmente y rezar para que el tráfico no derribara todo? Pues bien, esos días parecen cada vez más distantes.

En 2025, la combinación de serverless y edge computing transformó completamente cómo pensamos en infraestructura. Hoy, puedes escribir una función, hacer deploy, y estará corriendo en más de 300 localidades alrededor del mundo, respondiendo en menos de 50ms, escalando automáticamente de 0 a millones de solicitudes - todo sin gestionar un solo servidor.

¿Parece magia? Vamos a descubrir cómo funciona esta arquitectura en la práctica.

Serverless: Más Allá del Hype, La Realidad Práctica

Serverless no significa "sin servidores" - significa que no necesitas preocuparte con servidores. La infraestructura está abstraída, pagas solo por lo que usas, y la escala ocurre automáticamente.

El Modelo de Ejecución Serverless

Diferente de servidores tradicionales que corren 24/7, funciones serverless son event-driven:

  1. Cold Start: Primera invocación inicializa el ambiente de ejecución
  2. Warm: Ejecuciones subsecuentes reaprovechan el ambiente (si dentro de la ventana de tiempo)
  3. Scaling: Múltiples instancias son creadas automáticamente bajo demanda
  4. Billing: Pagas solo por el tiempo de ejecución (milisegundos)

Cuándo Serverless Realmente Brilla

APIs con Tráfico Irregular: Si tu API tiene picos de uso (e-commerce en Black Friday, por ejemplo), serverless escala automáticamente.

Procesamiento de Eventos: Procesamiento de imágenes, webhooks, jobs programados - perfectos para serverless.

Backends para JAMstack: Next.js, Nuxt, Remix - todos usan serverless functions para API routes.

Microservicios Ligeros: Cada función puede ser un microservicio independiente, deployado separadamente.

Edge Computing: Llevando Código Cerca del Usuario

Edge computing lleva serverless un paso más allá: en vez de correr en una región específica (us-east-1, por ejemplo), tu código corre en data centers esparcidos globalmente, próximos a tus usuarios.

Un usuario en Ciudad de México accede al edge node mexicano. Un usuario en Tokio accede al edge node japonés. Misma aplicación, latencia drásticamente reducida.

La Diferencia Entre Serverless Tradicional y Edge

AWS Lambda (Serverless Regional):

  • Ejecuta en una región específica (ej: us-east-1)
  • Cold start: 100-500ms
  • Latencia adicional para usuarios distantes

Cloudflare Workers (Edge Computing):

  • Ejecuta en 300+ localidades globalmente
  • Cold start: <10ms
  • Latencia consistentemente baja globalmente

Casos de Uso Ideales Para Edge

Personalización de Contenido: Modificar HTML basado en geolocalización, A/B testing, feature flags.

Autenticación y Autorización: Verificar tokens JWT antes de que requests lleguen al backend.

API Gateways: Enrutamiento inteligente, rate limiting, caching.

Middleware de Solicitudes: Headers, redirects, rewrite de URLs.

Cloudflare Workers: Edge Computing en la Práctica

Cloudflare Workers es la plataforma de edge computing más popular en 2025. Veamos un ejemplo práctico:

Ejemplo 1: API de Geolocalización con Cache Inteligente

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // Información de geolocalización disponible automáticamente
    const country = request.cf.country;
    const city = request.cf.city;
    const timezone = request.cf.timezone;

    // Cache key basado en localización
    const cacheKey = `geo:${country}:${city}`;

    // Verificar cache en KV (key-value store de Cloudflare)
    let data = await env.GEO_CACHE.get(cacheKey, { type: 'json' });

    if (!data) {
      // Buscar datos customizados para esa localización
      data = await fetchLocationData(country, city);

      // Cachear por 1 hora
      await env.GEO_CACHE.put(cacheKey, JSON.stringify(data), {
        expirationTtl: 3600,
      });
    }

    return new Response(JSON.stringify({
      location: { country, city, timezone },
      data,
      cached: !!data,
    }), {
      headers: {
        'Content-Type': 'application/json',
        'Cache-Control': 'public, max-age=3600',
      },
    });
  },
};

async function fetchLocationData(country, city) {
  // Simular búsqueda de datos personalizados
  return {
    currency: getCurrencyForCountry(country),
    language: getLanguageForCountry(country),
    popularProducts: await getPopularProducts(country),
    shippingOptions: await getShippingOptions(city),
  };
}

function getCurrencyForCountry(country) {
  const currencies = {
    BR: 'BRL',
    US: 'USD',
    JP: 'JPY',
    GB: 'GBP',
    MX: 'MXN',
    ES: 'EUR',
  };
  return currencies[country] || 'USD';
}

Este worker corre en el edge más próximo al usuario, detecta automáticamente la localización, y retorna datos personalizados con latencia mínima.

Ejemplo 2: Autenticación JWT en el Edge

import { verify } from '@tsndr/cloudflare-worker-jwt';

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // Rutas públicas no requieren auth
    const publicRoutes = ['/health', '/login', '/register'];
    if (publicRoutes.includes(url.pathname)) {
      return fetch(request);
    }

    // Extraer token del header
    const authHeader = request.headers.get('Authorization');
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return new Response('Unauthorized', { status: 401 });
    }

    const token = authHeader.substring(7);

    try {
      // Verificar JWT en el edge (¡sin llamar al backend!)
      const isValid = await verify(token, env.JWT_SECRET);

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

      // Decodificar payload para añadir al request
      const { payload } = jwt.decode(token);

      // Clonar request y añadir user info en los headers
      const modifiedRequest = new Request(request);
      modifiedRequest.headers.set('X-User-Id', payload.userId);
      modifiedRequest.headers.set('X-User-Role', payload.role);

      // Encaminar para origin
      return fetch(modifiedRequest);

    } catch (error) {
      return new Response('Token verification failed', { status: 401 });
    }
  },
};

Este enfoque verifica autenticación en el edge, antes de que la solicitud llegue a tu backend, economizando latencia y carga en los servidores.

AWS Lambda@Edge: Serverless en la CDN de Amazon

AWS Lambda@Edge permite correr funciones Lambda en los edge locations de CloudFront (200+ localizaciones).

Ejemplo: Redirección Inteligente Basada en Device

// Lambda@Edge para CloudFront
exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
  const headers = request.headers;

  // Detectar tipo de device
  const userAgent = headers['user-agent']?.[0]?.value || '';
  const isMobile = /mobile|android|iphone/i.test(userAgent);
  const isTablet = /tablet|ipad/i.test(userAgent);

  // Redirigir para versiones optimizadas
  if (request.uri === '/') {
    if (isMobile) {
      return {
        status: '302',
        statusDescription: 'Found',
        headers: {
          location: [{
            key: 'Location',
            value: '/mobile',
          }],
        },
      };
    }

    if (isTablet) {
      return {
        status: '302',
        statusDescription: 'Found',
        headers: {
          location: [{
            key: 'Location',
            value: '/tablet',
          }],
        },
      };
    }
  }

  // Modificar headers para optimización
  request.headers['x-device-type'] = [{
    key: 'X-Device-Type',
    value: isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop',
  }];

  return request;
};

Ejemplo: Generación de Imágenes Responsivas en el Edge

const sharp = require('sharp');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
  const response = event.Records[0].cf.response;

  // Extraer parámetros de query
  const params = new URLSearchParams(request.querystring);
  const width = parseInt(params.get('w') || '800');
  const quality = parseInt(params.get('q') || '80');
  const format = params.get('f') || 'webp';

  // Buscar imagen original del S3
  const s3Object = await s3.getObject({
    Bucket: 'my-images-bucket',
    Key: request.uri.substring(1), // remueve leading /
  }).promise();

  // Procesar imagen con Sharp
  const processedImage = await sharp(s3Object.Body)
    .resize(width, null, { withoutEnlargement: true })
    .toFormat(format, { quality })
    .toBuffer();

  // Retornar imagen procesada
  return {
    status: '200',
    statusDescription: 'OK',
    headers: {
      'content-type': [{
        key: 'Content-Type',
        value: `image/${format}`,
      }],
      'cache-control': [{
        key: 'Cache-Control',
        value: 'public, max-age=31536000, immutable',
      }],
    },
    body: processedImage.toString('base64'),
    bodyEncoding: 'base64',
  };
};

Vercel Edge Functions: Next.js en el Edge

Vercel Edge Functions son optimizadas para Next.js y corren en el edge globalmente.

Ejemplo: A/B Testing en el Edge

// app/middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
  // Bucket aleatorio para A/B test (0-99)
  const bucket = Math.floor(Math.random() * 100);

  // 50% de los usuarios ven variante A, 50% ven B
  const variant = bucket < 50 ? 'A' : 'B';

  // Clonar response para añadir cookie
  const response = NextResponse.next();

  // Almacenar variante en cookie (persistencia entre páginas)
  response.cookies.set('ab-test-variant', variant, {
    maxAge: 60 * 60 * 24 * 30, // 30 días
  });

  // Añadir header para analytics
  response.headers.set('X-AB-Test-Variant', variant);

  // Rewrite basado en la variante
  if (request.nextUrl.pathname === '/pricing') {
    if (variant === 'B') {
      return NextResponse.rewrite(new URL('/pricing-variant-b', request.url));
    }
  }

  return response;
}

export const config = {
  matcher: ['/pricing', '/checkout'],
};

Ejemplo: Rate Limiting en el Edge

import { NextResponse } from 'next/server';

const rateLimit = new Map();

export function middleware(request) {
  const ip = request.ip || 'unknown';
  const now = Date.now();
  const windowMs = 60 * 1000; // 1 minuto
  const maxRequests = 100;

  // Limpiar entradas antiguas
  for (const [key, value] of rateLimit.entries()) {
    if (now - value.timestamp > windowMs) {
      rateLimit.delete(key);
    }
  }

  // Verificar rate limit
  const userLimit = rateLimit.get(ip);

  if (!userLimit) {
    rateLimit.set(ip, { count: 1, timestamp: now });
  } else if (now - userLimit.timestamp > windowMs) {
    // Resetar ventana
    rateLimit.set(ip, { count: 1, timestamp: now });
  } else if (userLimit.count >= maxRequests) {
    // Límite excedido
    return new NextResponse('Rate limit exceeded', {
      status: 429,
      headers: {
        'Retry-After': String(Math.ceil((windowMs - (now - userLimit.timestamp)) / 1000)),
      },
    });
  } else {
    // Incrementar contador
    userLimit.count++;
  }

  return NextResponse.next();
}

Patrones de Arquitectura Serverless Moderna

1. Backend for Frontend (BFF) Pattern

// Cloudflare Worker actuando como BFF
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // Diferentes backends para diferentes clientes
    if (url.pathname.startsWith('/api/mobile')) {
      // Mobile recibe datos optimizados (menos campos)
      const data = await fetchFromBackend(env.MOBILE_API);
      return new Response(JSON.stringify({
        ...data,
        _optimized: true,
      }));
    }

    if (url.pathname.startsWith('/api/web')) {
      // Web recibe datos completos
      const data = await fetchFromBackend(env.WEB_API);
      return new Response(JSON.stringify(data));
    }

    return new Response('Not Found', { status: 404 });
  },
};

2. Event-Driven Serverless

// AWS Lambda triggered por S3 upload
exports.handler = async (event) => {
  const s3Event = event.Records[0].s3;
  const bucket = s3Event.bucket.name;
  const key = s3Event.object.key;

  console.log(`Processing file: ${key} from bucket: ${bucket}`);

  // Procesar imagen
  const originalImage = await s3.getObject({ Bucket: bucket, Key: key }).promise();

  // Generar thumbnails en paralelo
  const thumbnails = await Promise.all([
    generateThumbnail(originalImage, 200, 200),
    generateThumbnail(originalImage, 400, 400),
    generateThumbnail(originalImage, 800, 800),
  ]);

  // Upload thumbnails para S3
  await Promise.all(thumbnails.map((thumb, i) =>
    s3.putObject({
      Bucket: `${bucket}-thumbnails`,
      Key: `${key}-${[200, 400, 800][i]}px`,
      Body: thumb,
      ContentType: 'image/jpeg',
    }).promise()
  ));

  return { statusCode: 200, body: 'Thumbnails generated' };
};

3. Caching Estratégico en el Edge

export default {
  async fetch(request, env, ctx) {
    const cache = caches.default;
    const cacheKey = new Request(request.url, request);

    // Verificar cache primero
    let response = await cache.match(cacheKey);

    if (!response) {
      // Cache miss - buscar del backend
      response = await fetch(request);

      // Cachear si status es 200
      if (response.status === 200) {
        // Clonar response para cachear (puede ser consumida solo 1x)
        const responseToCache = response.clone();

        // Añadir al cache (asíncrono via waitUntil)
        ctx.waitUntil(cache.put(cacheKey, responseToCache));
      }
    }

    return response;
  },
};

Desafíos y Consideraciones Prácticas

Cold Starts: El Talón de Aquiles

Funciones serverless sufren de "cold starts" cuando quedan inactivas. Estrategias para mitigar:

1. Keep-Alive Pings: Invocar funciones periódicamente para mantenerlas warm
2. Provisioned Concurrency (AWS): Mantener instancias siempre warm (costo adicional)
3. Edge Computing: Workers tienen cold starts drásticamente menores (<10ms)

Límites de Ejecución

AWS Lambda: 15 minutos máximo
Cloudflare Workers: 50ms CPU time (plan free), 50ms-30s (plan pago)
Vercel Edge: 30 segundos

Para procesamientos largos, considera split en múltiples funciones o usar Step Functions.

Monitoreo y Debugging

Serverless dificulta debugging tradicional. Usa:

  • Structured Logging: JSON logs para fácil parsing
  • Distributed Tracing: AWS X-Ray, Datadog, Sentry
  • Metrics: CloudWatch, Prometheus para monitorear latencia y errores

El Futuro: Edge-First Architecture

En 2025, la tendencia es clara: edge-first. Aplicaciones modernas están siendo diseñadas desde el inicio para correr en el edge, aprovechando:

  • Baja latencia global: Usuarios en México y Japón tienen la misma experiencia
  • Escalabilidad automática: De 0 a millones sin configuración
  • Costo optimizado: Paga solo por lo que usas
  • Resiliencia: Distribuido globalmente, sin single point of failure

Si estás construyendo aplicaciones web modernas, serverless y edge computing ya no son "nice to have" - son esenciales para competir en performance y experiencia del usuario.

Si te sientes inspirado por el poder del serverless y edge computing, te recomiendo echar un vistazo a otro artículo: Microfrontends: Arquitectura Modular donde descubrirás cómo combinar edge computing con arquitectura de microfrontends para crear aplicaciones verdaderamente escalables.

¡Vamos a por ello! 🦅

📚 ¿Quieres Profundizar Tus Conocimientos en JavaScript?

Este artículo cubrió serverless y edge computing, pero hay mucho más por explorar en el mundo del desarrollo moderno.

Desarrolladores que invierten en conocimiento sólido y estructurado tienden a tener más oportunidades en el mercado.

Material de Estudio Completo

Si quieres dominar JavaScript de básico a avanzado, preparé una guía completa:

Opciones de inversión:

  • $9.90 USD (pago único)

👉 Conocer la Guía JavaScript

💡 Material actualizado con las mejores prácticas del mercado

Comentarios (0)

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

Añadir comentarios