WeatherToRun

Création d'une PWA haute performance avec Next.js Edge Functions, cache multicouche et algorithme météo personnalisé pour aider les coureurs à planifier leurs entraînements.

Next.jsTypeScriptPWAFonctions EdgePerformance
WeatherToRun

Le problème

Quand on court, on se pose la même question tous les jours: quand sortir et comment s'habiller? Les applis météo montrent des chiffres (température, humidité, vent), mais n'aident pas vraiment à décider. Je faisais des calculs mentaux chaque matin, et je me trompais parfois: trop couvert, pas assez, ou surpris par la pluie.

Comment j'ai construit la solution

WeatherToRun est parti d'une idée simple: et si l'application faisait ce calcul à ma place? Au lieu d'afficher des données brutes, elle analyse les conditions et donne une réponse claire: bon moment pour courir, ou non.

Le cœur du produit est un score de 1 à 100 combinant plusieurs facteurs météo. Un statut coloré ("Mauvais", "OK", "Bon") permet de décider en un coup d'œil.

Ce que j'ai construit:

  • Un algorithme basé sur la recherche: les pondérations sont justifiées. Température (30%) selon la plage optimale 44-59°F issue d'études marathon, vent (25%) car 10 mph de vent de face peut coûter 8-15 s/mile, point de rosée (20%) plus fiable que l'humidité pour le confort.
  • Edge-first pour la vitesse: APIs sur Vercel Edge Runtime, avec cold start ~50 ms au lieu de ~5000 ms en serverless classique.
  • Cache intelligent: cache horaire au edge, stale time client d'1h avec TanStack Query, et arrondi des coordonnées pour mutualiser les cache hits entre utilisateurs proches.
  • Recommandations de tenue personnalisées: un simple toggle "Plus de couches" / "Moins de couches" adapte les conseils.
  • PWA offline: installable et utilisable même sans connexion.

L'objectif était de construire un outil réellement utile, celui qu'on consulte juste avant de sortir courir.

Du croquis à l'interface

Avant d'écrire du code, j'ai fait des croquis papier. Que doit voir le coureur en premier? Le score et la tenue recommandée. Le reste vient après.

Croquis WeatherToRun 1 - vue principaleCroquis WeatherToRun 2 - structure des composantsCroquis WeatherToRun 3 - flux utilisateur

Wireframes initiaux explorant la vue principale, la structure des composants et le flux utilisateur.

Le mode hors ligne

Je voulais une expérience proche du natif: rapide, installable et robuste en connexion instable. Mais en PWA, tout ne se met pas en cache de la même manière.

Avec Workbox:

  • CacheFirst pour assets statiques (scripts, styles, images), expiration 30 jours.
  • NetworkFirst pour le manifest, timeout de 3 secondes.
  • NetworkOnly pour navigation et payloads React Server Components. J'avais essayé de les cacher, mais cela créait des incohérences d'état avec des données périmées.

Hors ligne, l'utilisateur voit une page dédiée au lieu d'une erreur navigateur.

Gérer le réel

Une appli météo doit être rapide, mais surtout fiable. APIs externes en panne, rate limits, réseau instable: il faut prévoir.

Quand ça casse

  • Retry avec backoff: en cas de 429 ou de coupure réseau, l'application réessaie avec délais progressifs (max 10 s).
  • Timeouts adaptés: API météo 8 s, AQI 3 s. Si AQI échoue, l'app continue avec la météo.

Le calcul du score

FacteurPoidsPourquoi
Température30%44-59°F = zone optimale de performance
Vent25%10 mph de face = 8-15 s/mile de perte
Point de rosée20%Meilleur indicateur d'inconfort que l'humidité
Précipitations15%Pluie, neige, orages
Indice UV10%Exposition solaire = stress thermique

L'algorithme gère aussi plus de 30 codes météo (orage, pluie verglaçante, etc.) avec pénalités adaptées, et combine rafales + vent soutenu (70/30).

SEO & découverte

J'ai ajouté JSON-LD, Open Graph dynamiques, sitemap et robots.txt pour améliorer la découvrabilité.

Observabilité

Sentry me permet de diagnostiquer les erreurs en production (contexte, coordonnées, réponse API) sans exposer de données utilisateurs.

Garder des données fraîches

Avec ISR time-based (2 h), la revalidation n'arrive qu'après une visite. Sur pages à faible trafic, cela donne parfois des données très anciennes.

J'ai donc mis en place une invalidation proactive: un cron appelle /api/revalidate toutes les 30 minutes.

Clés techniques:

  • Cache tags avec next: { tags: ['weather'] } puis revalidateTag('weather').
  • Stale-while-revalidate avec revalidateTag(tag, 'max') (Next.js 16) pour réponse instantanée.
  • GitHub Actions comme cron gratuit toutes les 30 minutes.
  • Fallback: un revalidate temporel reste actif en secours.

Endpoint protégé par secret (REVALIDATE_SECRET), sinon 401.

Résultat: données météo fraîches (max 30 min), même sur pages peu visitées.

Ce que j'ai appris

Moins de données, plus de synthèse. La valeur est dans l'interprétation.

La fiabilité est invisible jusqu'à ce qu'elle manque. L'utilisateur voit seulement si "ça marche".

Le cache est subtil. Certaines couches apportent beaucoup; d'autres cassent la cohérence.

L'Edge change la perception. Passer de secondes à millisecondes transforme l'expérience.

Une petite personnalisation suffit souvent. Un simple toggle peut créer une vraie sensation de pertinence.