Volver al blog

Edge Functions y el Futuro del Serverless: Arquitectura Moderna en 2025

Hola HaWkers, el mundo serverless evolucionó significativamente en los últimos años, y 2025 marca la consolidación de las Edge Functions como estándar para aplicaciones de alta performance. Si aún no estás familiarizado con edge computing o quieres entender cómo aprovechar esa tecnología, este artículo te guiará por los conceptos y prácticas más importantes.

¿Qué exactamente son Edge Functions? ¿Por qué ofrecen performance superior a las funciones serverless tradicionales? ¿Y cómo puedes implementarlas en tus proyectos? Vamos a explorar todo esto con ejemplos prácticos.

Qué Son Edge Functions

Edge Functions son funciones serverless que ejecutan en servidores distribuidos geográficamente, próximos a los usuarios finales. Diferente de funciones tradicionales que corren en regiones específicas, edge computing lleva la computación para el borde de la red.

Diferencia Entre Serverless Tradicional y Edge

Serverless Tradicional:

  • Ejecuta en regiones específicas (us-east-1, eu-west-1, etc.)
  • Latencia depende de la distancia del usuario al datacenter
  • Cold start puede ser significativo (centenas de ms a segundos)
  • Ambiente Node.js completo generalmente disponible

Edge Functions:

  • Ejecutan en centenas de puntos de presencia globales
  • Latencia consistentemente baja independiente de la localización
  • Cold start mínimo (pocos milisegundos)
  • Runtime optimizado basado en V8 o similar

💡 Contexto: Para un usuario en España accediendo una función en us-east-1, la latencia de red puede ser 100-200ms. Con edge, esa latencia cae para 10-30ms.

Por Qué Edge Functions Dominan en 2025

La adopción de edge functions creció exponencialmente por varias razones:

1. Performance Global

Con servidores en más de 300 locales alrededor del mundo, usuarios de cualquier lugar tienen latencia similar y baja.

2. Costo-Beneficio

A pesar del costo por ejecución similar, la menor latencia significa menos tiempo de espera y mejor experiencia, justificando la inversión.

3. Casos de Uso Expandidos

Inicialmente usadas solo para redirecciones simples, ahora soportan:

Casos de uso modernos:

  • Autenticación y autorización
  • Personalización de contenido
  • A/B testing
  • Rate limiting
  • Manipulación de headers
  • Renderización server-side
  • APIs completas

4. Ecosistema Maduro

Plataformas como Cloudflare Workers, Vercel Edge Functions, Deno Deploy y AWS CloudFront Functions maduraron significativamente.

Implementando Edge Functions en la Práctica

Vamos a ver ejemplos prácticos usando diferentes plataformas.

Vercel Edge Functions

Perfectas para proyectos Next.js:

// pages/api/geo.ts o app/api/geo/route.ts
import { NextRequest, NextResponse } from 'next/server';

export const config = {
  runtime: 'edge', // Define que es edge function
};

export default function handler(req: NextRequest) {
  // Informaciones de geolocalización disponibles automáticamente
  const country = req.geo?.country ?? 'Desconocido';
  const city = req.geo?.city ?? 'Desconocida';
  const region = req.geo?.region ?? 'Desconocida';

  return NextResponse.json({
    message: `¡Hola visitante de ${city}, ${region}, ${country}!`,
    latency: 'Esta respuesta vino del edge más próximo a ti',
    timestamp: new Date().toISOString(),
  });
}

Cloudflare Workers

Una de las plataformas más populares para edge:

// worker.ts
export interface Env {
  MY_KV: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    // Rate limiting simple usando KV
    const ip = request.headers.get('CF-Connecting-IP') ?? 'unknown';
    const rateLimitKey = `ratelimit:${ip}`;

    const currentCount = await env.MY_KV.get(rateLimitKey);
    const count = currentCount ? parseInt(currentCount) : 0;

    if (count > 100) {
      return new Response('Rate limit exceeded', { status: 429 });
    }

    // Incrementa contador con TTL de 1 minuto
    ctx.waitUntil(
      env.MY_KV.put(rateLimitKey, String(count + 1), {
        expirationTtl: 60
      })
    );

    // Lógica principal
    if (url.pathname === '/api/data') {
      const data = await fetchDataFromOrigin();
      return new Response(JSON.stringify(data), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

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

async function fetchDataFromOrigin() {
  // Simula búsqueda de datos
  return { message: 'Datos del edge', timestamp: Date.now() };
}

Deno Deploy

Plataforma nativa para edge con Deno:

// server.ts
import { serve } from 'https://deno.land/std/http/server.ts';

interface RequestInfo {
  method: string;
  url: string;
  userAgent: string | null;
  region: string | null;
}

serve(async (request: Request): Promise<Response> => {
  const requestInfo: RequestInfo = {
    method: request.method,
    url: request.url,
    userAgent: request.headers.get('user-agent'),
    region: Deno.env.get('DENO_REGION') ?? 'unknown',
  };

  // Ruteo simple
  const url = new URL(request.url);

  switch (url.pathname) {
    case '/':
      return new Response('Hello from Deno Edge!', {
        headers: { 'content-type': 'text/plain' },
      });

    case '/api/info':
      return new Response(JSON.stringify(requestInfo), {
        headers: { 'content-type': 'application/json' },
      });

    case '/api/time':
      const now = new Date();
      return new Response(
        JSON.stringify({
          utc: now.toUTCString(),
          iso: now.toISOString(),
          region: requestInfo.region,
        }),
        { headers: { 'content-type': 'application/json' } }
      );

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

Patrones Avanzados con Edge Functions

Middleware de Autenticación

// middleware.ts (Next.js Edge Middleware)
import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose';

const PUBLIC_PATHS = ['/', '/login', '/api/auth'];
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET);

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // Permite rutas públicas
  if (PUBLIC_PATHS.some(path => pathname.startsWith(path))) {
    return NextResponse.next();
  }

  // Verifica token JWT
  const token = request.cookies.get('auth-token')?.value;

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    const { payload } = await jwtVerify(token, JWT_SECRET);

    // Añade info del usuario a los headers
    const response = NextResponse.next();
    response.headers.set('x-user-id', payload.sub as string);
    response.headers.set('x-user-role', payload.role as string);

    return response;
  } catch (error) {
    // Token inválido
    const response = NextResponse.redirect(new URL('/login', request.url));
    response.cookies.delete('auth-token');
    return response;
  }
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico).*)',
  ],
};

A/B Testing en el Edge

// Cloudflare Worker para A/B testing
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    // Verifica cookie existente o atribuye variante
    const cookies = request.headers.get('Cookie') ?? '';
    let variant = getCookie(cookies, 'ab-variant');

    if (!variant) {
      // Atribuye variante aleatoriamente (50/50)
      variant = Math.random() < 0.5 ? 'A' : 'B';
    }

    // Modifica respuesta basado en la variante
    const response = await fetch(request);
    const html = await response.text();

    let modifiedHtml = html;
    if (variant === 'B') {
      // Aplica cambios de la variante B
      modifiedHtml = html.replace(
        '<button class="cta">Comprar Ahora</button>',
        '<button class="cta-new">Añadir al Carrito</button>'
      );
    }

    const newResponse = new Response(modifiedHtml, response);

    // Define cookie para mantener consistencia
    newResponse.headers.append(
      'Set-Cookie',
      `ab-variant=${variant}; Path=/; Max-Age=604800`
    );

    return newResponse;
  },
};

function getCookie(cookies: string, name: string): string | null {
  const match = cookies.match(new RegExp(`${name}=([^;]+)`));
  return match ? match[1] : null;
}

Limitaciones y Consideraciones

Qué Edge Functions No Hacen Bien

Limitaciones importantes:

  • Tiempo de ejecución limitado (generalmente 30s máximo, algunos 50ms)
  • Memoria restringida (128MB típico)
  • Sin acceso a filesystem
  • APIs limitadas (sin Node.js nativo completo)
  • Conexiones de larga duración problemáticas

Cuándo Usar Serverless Tradicional

Prefiere serverless tradicional para:

  • Procesamiento intensivo de CPU
  • Operaciones de base de datos complejas
  • Tareas de larga duración
  • Cuando necesitas ecosistema Node.js completo
  • Procesamiento de archivos grandes

Arquitectura Híbrida

El mejor abordaje generalmente combina edge y serverless:

Usuario

Edge Function (autenticación, cache, personalización)

Serverless Function (lógica de negocio, base de datos)

Base de Datos / Servicios

Comparativo de Plataformas

Plataforma Cold Start Locales Lenguajes Uso Gratuito
Cloudflare Workers ~0ms 300+ JS/TS, Rust, WASM 100k req/día
Vercel Edge ~1ms 100+ JS/TS Conforme plan
Deno Deploy ~1ms 35+ JS/TS, WASM 1M req/mes
AWS CloudFront ~5ms 400+ JS Conforme uso
Fastly Compute ~5ms 70+ JS, Rust, WASM Conforme uso

Eligiendo la Plataforma Correcta

Cloudflare Workers:

  • Mejor para aplicaciones standalone
  • Ecosistema más maduro (KV, D1, R2)
  • Precios competitivos

Vercel Edge:

  • Ideal para proyectos Next.js
  • Integración perfecta con framework
  • Edge Middleware poderoso

Deno Deploy:

  • Óptimo para quien prefiere Deno
  • Deploy vía GitHub simple
  • TypeScript first-class

Mejores Prácticas

1. Cache Agresivo

// Implementa cache para respuestas que pueden ser cacheadas
export default {
  async fetch(request: Request): Promise<Response> {
    const cacheKey = new Request(request.url, request);
    const cache = caches.default;

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

    if (!response) {
      response = await fetch(request);

      // Clone para cachear
      response = new Response(response.body, response);
      response.headers.set('Cache-Control', 'public, max-age=3600');

      // Cache en background
      ctx.waitUntil(cache.put(cacheKey, response.clone()));
    }

    return response;
  },
};

2. Tratamiento de Errores Robusto

export default {
  async fetch(request: Request): Promise<Response> {
    try {
      return await handleRequest(request);
    } catch (error) {
      console.error('Edge function error:', error);

      // Retorna respuesta de error amigable
      return new Response(
        JSON.stringify({
          error: 'Internal Error',
          message: 'Something went wrong',
        }),
        {
          status: 500,
          headers: { 'Content-Type': 'application/json' },
        }
      );
    }
  },
};

3. Monitoreo y Observabilidad

// Añade logging estructurado
interface LogEntry {
  timestamp: string;
  level: 'info' | 'warn' | 'error';
  message: string;
  metadata?: Record<string, unknown>;
}

function log(entry: LogEntry) {
  console.log(JSON.stringify(entry));
}

export default {
  async fetch(request: Request): Promise<Response> {
    const startTime = Date.now();

    const response = await handleRequest(request);

    log({
      timestamp: new Date().toISOString(),
      level: 'info',
      message: 'Request handled',
      metadata: {
        url: request.url,
        method: request.method,
        status: response.status,
        duration: Date.now() - startTime,
      },
    });

    return response;
  },
};

Conclusión

Edge Functions representan una evolución significativa en la arquitectura serverless, ofreciendo latencia consistentemente baja para usuarios globales. En 2025, se tornaron componente esencial para aplicaciones que priorizan performance.

La clave es entender cuándo usar edge versus serverless tradicional. Para autenticación, personalización, rate limiting y respuestas rápidas, edge es ideal. Para procesamiento pesado y lógica compleja de negocio, serverless tradicional continúa siendo la elección correcta.

La combinación de ambos ofrece lo mejor de los dos mundos: respuestas rápidas en el edge con poder de procesamiento en el backend cuando necesario.

Si quieres profundizarte en arquitecturas modernas, recomiendo que revises otro artículo: Waymo Suspende Robotaxis en San Francisco donde vas a descubrir cómo construir experiencias web modernas y performáticas.

¡Vamos a por ello! 🦅

Comentarios (0)

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

Añadir comentarios