WeatherToRun

Construindo uma PWA de alta performance com Edge Functions no Next.js, cache em múltiplas camadas e um algoritmo próprio de pontuação meteorológica para ajudar corredores a planear os treinos.

Next.jsTypeScriptPWAEdge FunctionsPerformance
WeatherToRun

O problema

Se corre, conhece a luta diária: quando devo correr e o que devo vestir? A maioria das aplicações de meteorologia só mostra números (temperatura, humidade, vento), mas não ajuda verdadeiramente na decisão. Eu via-me a fazer contas mentais todas as manhãs, equilibrando variáveis na cabeça. Às vezes errava e saía com roupa a mais, com frio, ou apanhado por chuva inesperada.

Como construí a solução

O WeatherToRun começou com uma ideia simples: e se a aplicação fizesse essa conta mental por mim? Em vez de mostrar dados brutos e deixar a interpretação para o utilizador, analisa as condições e dá uma resposta direta: é um bom momento para correr ou não?

O núcleo da aplicação é um sistema de pontuação que combina vários fatores numa nota de 1 a 100. Uma classificação por cores ("Mau", "OK" ou "Bom") acelera ainda mais a decisão.

O que eu construí:

  • Algoritmo de pontuação baseado em pesquisa: Estudei como o clima afeta a performance de corrida. Os pesos não são arbitrários: temperatura tem 30% porque a faixa ideal (44-59°F) vem de estudos de maratona; vento tem 25% porque um vento contrário de 10 mph pode reduzir o ritmo em 8-15 s/milha; ponto de orvalho (20%) se mostrou melhor que umidade para prever conforto.
  • Edge-first para velocidade: As rotas de API rodam no Edge Runtime da Vercel, com cold starts por volta de ~50 ms em vez de ~5000 ms de serverless tradicional.
  • Cache inteligente em toda parte: Estratégia em múltiplas camadas: cache horário no edge, stale time de 1 hora no cliente com TanStack Query e arredondamento de coordenadas para 4 casas decimais para aumentar cache hit entre utilizadores próximos.
  • Recomendação de roupa personalizada: Um toggle simples de "Mais camadas" / "Menos camadas" permite ajustar as sugestões ao seu perfil térmico.
  • PWA com suporte offline: Funciona sem conexão e pode ser instalado no celular.

O objetivo era criar algo realmente útil, algo que eu de fato usaria antes de sair para correr.

Do esboço à interface

Antes de escrever código, peguei em papel e caneta e desenhei como a aplicação deveria funcionar. O que o corredor precisa de ver primeiro? A pontuação e o que vestir. O resto é secundário. Estes wireframes ajudaram a definir hierarquia sem me perder em detalhes de implementação.

Esboço 1 do WeatherToRun - exploração da visão principalEsboço 2 do WeatherToRun - estrutura de componentesEsboço 3 do WeatherToRun - fluxo de usuário

Wireframes iniciais explorando tela principal, estrutura de componentes e fluxo de usuário.

Fazendo funcionar offline

Eu queria que o WeatherToRun se comportasse como uma aplicação nativa: instalável, rápida e funcional com ligação instável. Mas cache de PWA não é "cachear tudo".

Usei Workbox com estratégias diferentes por tipo de recurso:

  • CacheFirst para estáticos (scripts, estilos, imagens), com expiração de 30 dias.
  • NetworkFirst para o manifest, com timeout de 3 segundos.
  • NetworkOnly para navegação e payload de React Server Components. Eu inicialmente cacheei isso, mas tive inconsistências com dados stale e estados estranhos na UI entre dispositivos.

Quando está offline, o utilizador vê uma página amigável de offline em vez de erro do navegador. A aplicação pode ser instalada no ecrã inicial e corre em ecrã inteiro.

Lidando com o mundo real

Apps de clima precisam ser rápidos e confiáveis. APIs externas caem, limites de taxa aparecem, rede falha. Eu investi bastante para tratar isso com degradação elegante.

Quando algo dá errado

  • Retry com backoff: Em rate limit (429) ou falha momentânea de rede, a aplicação tenta novamente com atrasos progressivos, até 10 segundos.
  • Timeouts coerentes: API de clima com 8 segundos; qualidade do ar (AQI) com 3 segundos. Se AQI falhar, a aplicação segue com dados de clima.

A matemática da pontuação

FatorPesoPor que importa
Temperatura30%44-59°F é a faixa ideal para performance
Vento25%Vento contrário de 10 mph custa 8-15 s/milha
Ponto de orvalho20%Melhor que umidade para prever desconforto
Precipitação15%Chuva, neve, tempestades
Índice UV10%Exposição ao sol aumenta estresse térmico

O algoritmo também lida com mais de 30 códigos climáticos, de tempestades a chuva congelante, cada um com penalidade adequada. Também combino rajadas e vento sustentado (70/30) para refletir melhor o mundo real.

Descoberta e SEO

Implementei JSON-LD em tipos de página relevantes, Open Graph dinâmico, sitemap e robots.txt configurados para melhorar descoberta orgânica.

Observabilidade

Integrei Sentry para monitorar falhas em produção com contexto de operação, coordenadas e respostas de API, sem expor dados sensíveis.

Mantendo dados de clima atualizados

No início usei ISR por tempo com revalidação de 2 horas. O problema: a revalidação só acontece quando alguém visita após o período stale. Em páginas com pouco tráfego, isso gera dados antigos no primeiro acesso.

A solução foi invalidar cache proativamente com agendamento. Criei um cron que chama /api/revalidate a cada 30 minutos.

Pontos-chave:

  • Cache tags: next: { tags: ['weather'] } nas chamadas de API e invalidação com revalidateTag('weather').
  • Stale-while-revalidate: com revalidateTag(tag, 'max') no Next.js 16, o utilizador recebe conteúdo imediato enquanto a atualização corre em segundo plano.
  • GitHub Actions como cron gratuito: workflow executando a cada 30 minutos para chamar o endpoint de revalidação.
  • Degradação elegante: se o cron falhar, o fallback de revalidate por tempo ainda cobre.

O endpoint é protegido por token secreto (REVALIDATE_SECRET). Sem ele, retorna 401.

Com isso, os dados ficam no máximo 30 minutos defasados, mesmo em páginas de baixo tráfego.

O que aprendi

Menos dados, mais síntese. O valor não estava em exibir mais informação, e sim em sintetizar melhor.

Confiabilidade é invisível até falhar. O utilizador não vê retry ou timeout, só percebe se "funciona".

Cache é mais difícil do que parece. Estratégias em múltiplas camadas evoluíram com padrão de uso real.

Edge muda tudo. Sair de ~5 s para ~50 ms em cold start muda completamente a percepção de rapidez.

Pequena personalização gera grande impacto. Um toggle de camadas já fez a recomendação parecer pessoal sem complicar a UX.