WeatherToRun

Construcción de una PWA de alto rendimiento con Next.js Edge Functions, caché multicapa y un algoritmo personalizado de puntuación del clima para ayudar a corredores a planificar entrenamientos.

Next.jsTypeScriptPWAFunciones EdgeRendimiento
WeatherToRun

El problema

Si corres, conoces la duda diaria: ¿cuándo salir y qué ropa usar? La mayoría de apps del tiempo solo muestran números (temperatura, humedad, viento), pero no ayudan a decidir. Yo hacía cálculos mentales cada mañana y, a veces, me equivocaba: demasiado abrigo, poco abrigo o lluvia inesperada.

Cómo construí la solución

WeatherToRun nació de una idea simple: ¿y si la app hiciera ese cálculo por mí? En lugar de mostrar datos en bruto, analiza las condiciones y responde de forma directa: ¿es buen momento para correr o no?

El núcleo es un sistema de puntuación de 1 a 100 que combina varios factores meteorológicos. Además, una etiqueta por color ("Malo", "OK", "Bueno") acelera la decisión.

Lo que construí:

  • Algoritmo basado en evidencia: temperatura (30%) por rango óptimo 44-59°F en estudios de maratón; viento (25%) porque 10 mph en contra puede costar 8-15 s/milla; punto de rocío (20%) por ser mejor predictor de confort que la humedad.
  • Edge-first para velocidad: rutas API en Vercel Edge Runtime con cold starts ~50 ms frente a ~5000 ms en serverless tradicional.
  • Caché inteligente: caché horaria en edge, stale time de 1 hora en cliente con TanStack Query y redondeo de coordenadas para compartir cache hits entre usuarios cercanos.
  • Recomendación de ropa personalizada: interruptor "Más capas" / "Menos capas".
  • PWA offline: instalable y funcional sin conexión.

El objetivo fue construir algo realmente útil para el momento justo antes de salir a correr.

Del boceto a la UI

Antes de programar, hice bocetos en papel. ¿Qué necesita ver primero un corredor? Puntuación y recomendación de ropa. Lo demás es secundario.

Boceto 1 de WeatherToRun - exploración de vista principalBoceto 2 de WeatherToRun - estructura de componentesBoceto 3 de WeatherToRun - flujo de usuario

Wireframes iniciales para explorar vista principal, estructura de componentes y flujo.

Funcionamiento offline

Quería una experiencia tipo nativa: instalable, rápida y usable con mala conexión. Pero en PWA no conviene cachear todo.

Con Workbox:

  • CacheFirst para estáticos (scripts, estilos, imágenes), con 30 días de expiración.
  • NetworkFirst para el manifest, con timeout de 3 segundos.
  • NetworkOnly para navegación y payloads de React Server Components. Al cachearlos tuve estados inconsistentes por datos desactualizados.

En modo offline, se muestra una pantalla amigable en lugar del error del navegador.

Preparado para el mundo real

Una app del tiempo debe ser rápida y confiable. APIs externas fallan, hay límites de tasa y redes inestables.

Cuando algo falla

  • Reintentos con backoff: ante 429 o fallos de red, la app reintenta con esperas crecientes hasta 10 s.
  • Timeouts razonables: 8 s para clima, 3 s para AQI. Si AQI falla, la app sigue con datos de clima.

La lógica de puntuación

FactorPesoPor qué importa
Temperatura30%44-59°F es la zona ideal de rendimiento
Viento25%10 mph en contra cuesta 8-15 s/milla
Punto de rocío20%Mejor predictor de incomodidad que la humedad
Precipitación15%Lluvia, nieve, tormentas
Índice UV10%Aumenta el estrés térmico

También contempla más de 30 códigos meteorológicos y combina ráfagas con viento sostenido (70/30) para una lectura más realista.

Descubrimiento y SEO

Añadí JSON-LD, Open Graph dinámico, sitemap y robots.txt para mejorar descubrimiento orgánico.

Observabilidad

Integré Sentry para capturar fallos en producción con contexto útil sin exponer datos de usuarios.

Datos siempre frescos

Con ISR por tiempo (2 h), la revalidación depende de visitas. En páginas con poco tráfico esto puede dejar datos antiguos.

Solución: invalidación proactiva cada 30 minutos mediante cron contra /api/revalidate.

Piezas clave:

  • Cache tags con next: { tags: ['weather'] } y revalidateTag('weather').
  • Stale-while-revalidate con revalidateTag(tag, 'max') en Next.js 16.
  • GitHub Actions como cron gratuito cada 30 minutos.
  • Fallback de revalidate por tiempo si el cron falla.

El endpoint está protegido con REVALIDATE_SECRET; sin token válido responde 401.

Resultado: datos con un desfase máximo de 30 minutos.

Lo que aprendí

Menos datos, más síntesis. El valor está en interpretar, no en mostrar más.

La fiabilidad es invisible hasta que falta. El usuario solo nota si funciona.

El caché es delicado. Algunas capas ayudan mucho; otras rompen coherencia.

Edge cambia la experiencia. Pasar de segundos a milisegundos se siente de inmediato.

Pequeña personalización, gran impacto. Un simple toggle ya mejora mucho la utilidad.