Web app con Next.js: case study
Case study: piattaforma cicloturismo full-stack con Next.js 16, Supabase, Leaflet e Web Worker. Performance A+, admin MFA e build self-healing.
GeoCycleTour è una piattaforma di cicloturismo full-stack che ho progettato e sviluppato da zero: mappe interattive con tracce GPX, pannello admin con autenticazione MFA e un sistema di build che non fallisce mai. In questo case study racconto le scelte architetturali, i problemi tecnici risolti e i risultati ottenuti.
Il progetto in numeri
| Metrica | Valore |
|---|---|
| Performance desktop | 105/100 (Lighthouse) |
| Performance mobile | 97/100 |
| Stack | Next.js 16, React 19, TypeScript, Supabase |
| Feature principali | Mappe interattive, GPX parsing, admin MFA, CSV import |
| Testing | Playwright E2E + GitHub Actions CI/CD |
Lo stack tecnologico
Next.js 16 + React 19
Ho scelto Next.js 16 per le feature avanzate che offre: Server Components di default (zero JavaScript inutile al client), Partial Prerendering (contenuto statico + dinamico nella stessa pagina) e la direttiva 'use cache' per caching server-side granulare.
Il risultato: pagine che si caricano istantaneamente con il contenuto statico pre-renderizzato, mentre i dati dinamici arrivano via Suspense senza bloccare il rendering.
Supabase: database, auth e storage
Supabase gestisce tre aspetti critici dell'applicazione:
- PostgreSQL con Row Level Security per proteggere i dati a livello di riga
- Auth con MFA/TOTP per l'accesso admin (password + codice temporaneo)
- Storage per i file GPX con proxy via Vercel serverless function
Ho implementato una strategia a 3 client: un client server per Server Components, un client browser solo per le operazioni di autenticazione e un layer di query cachate con revalidazione automatica tramite tag.
Leaflet.js per le mappe interattive
Leaflet è la libreria di mappe open-source più leggera e flessibile. L'integrazione con Next.js richiede attenzione: Leaflet non supporta il rendering lato server, quindi va caricato con import dinamico e ssr: false. Le tracce GPX vengono renderizzate come polyline sulla mappa, con marker per i punti di interesse.
Le sfide tecniche risolte
Web Worker per il parsing GPX
I file GPX dei percorsi ciclabili possono superare i 500 KB di coordinate XML. Parsarli nel thread principale blocca l'interfaccia — la mappa si congela e i pulsanti non rispondono.
La soluzione: un Web Worker che esegue il parsing in un thread separato. Il componente invia il file al worker, che restituisce le coordinate processate senza mai bloccare la UI. Per i browser che non supportano i Web Worker, un parser sincrono di fallback garantisce la compatibilità.
Build self-healing
Problema reale: le build di Vercel possono andare in timeout durante la connessione a Supabase. Un errore di rete durante il deploy significherebbe un sito rotto.
La soluzione è un pattern self-healing in tre passi:
- Build: prova a fetchare i dati da Supabase
- Fallback: se la connessione fallisce, usa dati statici di placeholder
- Runtime: il client rileva il fallback e chiama un API route (
/api/homepage-data) per ottenere i dati reali
Il risultato: la build non fallisce mai, e il contenuto reale appare dopo circa 200ms dall'idratazione. L'utente non nota la differenza.
Admin SPA con URL come stato
Il pannello admin gestisce percorsi, partner, punti di interesse e import CSV. Invece di navigare tra pagine diverse (che causano perdita di stato e ricaricamenti), ho implementato un pattern SPA:
- L'URL è la fonte di verità:
?tab=percorsi&editId=123 - Le navigazioni usano
router.push()senza ricaricare la pagina - Il tasto "Indietro" del browser torna alla lista — comportamento nativo
- I form di modifica si aprono inline, senza perdere il contesto
Pipeline di import CSV
Gli utenti admin possono importare percorsi da file CSV. La pipeline gestisce:
- Formati coordinate: la funzione
normalizeCoordinate()gestisce sia il formato italiano (45,123) che inglese (45.123) - Validazione: ogni riga viene verificata con Zod prima dell'inserimento
- Report errori: se alcune righe non sono valide, genera un JSON scaricabile con il dettaglio dei problemi
Performance: come ho raggiunto A+
Desktop 105/100 e mobile 97/100 non sono un caso. Le ottimizzazioni principali:
- Server Components di default: il JavaScript del client contiene solo il codice interattivo (mappe, form, navigazione). Tutto il resto è HTML statico
- Partial Prerendering: le pagine dei percorsi hanno il layout pre-renderizzato staticamente, con i dati dinamici (ultimo aggiornamento, disponibilità) caricati via Suspense
- next/image con WebP: tutte le immagini servite in formato moderno con dimensioni responsive
- Inline CSS: eliminazione del CSS render-blocking con
experimental.inlineCss: true - Cache aggressiva: 1 anno per asset statici, revalidazione on-demand per i dati
Sicurezza e accessibilità
Autenticazione MFA
L'accesso admin richiede due fattori: password e codice TOTP (Time-based One-Time Password) generato da un'app come Google Authenticator. Non basta rubare una password per accedere.
Row Level Security
Ogni tabella Supabase ha policy RLS che determinano chi può leggere, inserire, modificare e cancellare. Anche se qualcuno ottenesse l'API key pubblica, non potrebbe modificare dati senza autenticazione valida.
Accessibilità WCAG 2.1 AA
La piattaforma è conforme alle linee guida WCAG: contrasto testo verificato, navigazione da tastiera completa, label ARIA su tutti gli elementi interattivi e alternative testuali per le immagini.
Backup automatici
Un workflow GitHub Actions esegue backup settimanali del database PostgreSQL e dello storage Supabase. I backup vengono conservati come artifact per 90 giorni. Se qualcosa va storto, posso ripristinare lo stato completo dell'applicazione in pochi minuti.
Cosa ho imparato
Server Components cambiano tutto
Con React Server Components, il paradigma si inverte: il JavaScript va al client solo quando serve. Su GeoCycleTour, le pagine informative sono zero-JS — solo HTML e CSS. Il JavaScript arriva solo per le mappe interattive e i form.
I Web Worker sono sottovalutati
Per operazioni computazionalmente pesanti (parsing, elaborazione dati, crittografia), i Web Worker eliminano il problema del thread singolo di JavaScript. Su GeoCycleTour, la differenza tra parsing sincrono e asincrono è la differenza tra una mappa che si blocca e una che resta fluida.
La resilienza va progettata, non aggiunta
Il pattern self-healing non è un hotfix: è una decisione architetturale. Progettare per il fallimento — connessione persa, servizio esterno down, timeout — rende l'applicazione robusta in produzione senza interventi manuali.
Vuoi sviluppare una web app simile?
Se hai bisogno di una piattaforma con mappe interattive, gestione dati, pannello admin e performance di primo livello, parliamone. Ogni progetto parte da un'analisi dei requisiti e un preventivo gratuito — senza impegno.