# BLOCKv: dashboard in tempo reale

Source: <https://www.nfw.run/casi-studio/blockv-dashboard>

[Home](https://www.nfw.run/) [Casi studio](https://www.nfw.run/casi-studio)

BLOCKv

Una dashboard Web3, che acquisisce e arricchisce oltre 3 milioni di transazioni on-chain e le trasmette in tempo reale via SSE, mantenendo quasi istantanea ogni query su cui un visitatore aspetta: feed live in \~16 ms con il 99% di cache hit, e un 100/100 pieno su accessibilità, best practices e SEO.

3M+ transazioni, query critiche sotto i 100 ms

Pubblicato: 1 giugno 2025Scritto da [Enrico Pasetto](https://www.linkedin.com/in/enrico-pasetto) (<enrico@nfw.run>)[Visita il progetto live](https://dashboard.blockv.io)

![Schermata di BLOCKv: dashboard in tempo reale](https://www.nfw.run/portfolio/blockv-dashboard.png)

## Il problema

Il cliente voleva una dashboard per mostrare in tempo reale creazioni, trasferimenti ed eliminazioni di SmartNFT (una sorta di biglietto). Oltre agli aggiornamenti live, qualsiasi visitatore doveva poter avere una visione panoramica dell'intero ecosistema: dati storici, prezzo del token, totale in staking e valori aggregati derivati da tutto quanto sopra descritto.

## Prime valutazioni

La prima priorità del progetto era quella di servire al visitatore la stragrande maggioranza delle informazioni con una latenza molto bassa. Per quanto riguarda asset come immagini, video, script e così via, la risposta più sensata era una CDN, e quella di Vercel avrebbe coperto alla perfezione questa necessità sia per clienti dentro che fuori Europa.

Oltre a questo, contava altrettanto dove risiedesse il backend (e quanto vicino fosse al database). Anche questo problema era risolvibile in pochi click, dato che su Vercel un amministratore può scegliere dove ospitare le funzioni Next.js. Persino in più luoghi contemporaneamente.

Per il database, inizialmente avevamo puntato su TimescaleDB, perché è pensato esattamente per questo tipo di lavoro: operazioni su una grandissima mole di dati strutturati in serie temporali tramite hypertable. Il problema principale, però, era il costo: la versione cloud, soprattutto con le read replica di cui avremmo avuto bisogno, era completamente fuori da qualsiasi budget ragionevole. Per costruire una prima versione funzionante del progetto, però, l'abbiamo usato comunque, così da avere la certezza che le prestazioni del database non avrebbero fatto da collo di bottiglia.

Ci serviva anche uno scraper (programma che cercasse, analizzasse e salvasse una grande mole di dati) capace di condividere rapidamente i dati raccolti on-chain con altri server che li avrebbero propagati ai visitatori. Per questo abbiamo scelto Bun. Sia per le prestazioni che per la facilità di implementazione.

Infine, per i server che dovevano inviare le informazioni in tempo reale ai visitatori, abbiamo optato per delle istanze VPS. Qualsiasi altra opzione era semplicemente troppo dispendiosa in termini di tempo di sviluppo e mantenimento per essere giustificata.

L'architettura finale si può riassumere così:

1. **Scraper**: controlla i dati on-chain e invia dati pronti alla visualizzazione a un server principale.
1. **Server principale**: può esso stesso gestire l'invio di dati in tempo reale, oppure essere configurato per condividere i dati solo con altri server di replica (per scalabilità).
1. **Server di replica**: ha l'unico scopo di inviare dati in tempo reale tramite Server-Sent Events (SSE) a chi visualizza il sito.
1. **Backend**: gestisce caching e relativa invalidazione, query verso servizi esterni come CoinGecko e altre chiamate API.
1. **Client**: l'interfaccia completa, costruita con Next.js, TailwindCSS e TypeScript. Questa combinazione ci ha permesso di sviluppare il tutto in modo estremamente efficiente.

## Perché Server-Sent Events e non WebSocket?

Perché i dati viaggiano in una sola direzione. La dashboard invia i nuovi eventi SmartNFT dal server al visitatore, e il visitatore non deve mai rimandare nulla sullo stesso canale: filtri, ricerche e paginazione sono normali richieste HTTP al backend. I Server-Sent Events (SSE) sono pensati esattamente per questo caso, mentre i WebSocket risolvono un problema che questo progetto non aveva.

|                      | Server-Sent Events                       | WebSocket                                              |
| -------------------- | ---------------------------------------- | ------------------------------------------------------ |
| Direzione            | Solo dal server al client                | In entrambe le direzioni                               |
| Protocollo           | HTTP standard                            | Un protocollo a parte, ottenuto con un upgrade da HTTP |
| Riconnessione        | Integrata nell'`EventSource` del browser | Da scrivere a mano                                     |
| Ripresa dello stream | Header `Last-Event-ID`, standard         | Da scrivere a mano                                     |
| Contenuto            | Testo (il JSON va benissimo)             | Testo o binario                                        |
| Ideali per           | Feed live, notifiche, dashboard          | Chat, multiplayer, editing collaborativo               |

In pratica, scegliere gli SSE ci ha dato quattro vantaggi:

- **Server più semplici.** Un server di replica deve solo tenere aperta una risposta HTTP e scriverci dentro gli eventi. Per questo, come raccontato più sotto, sono bastate pochissime righe di codice.
- **Nessuna configurazione particolare nel mezzo.** Gli SSE sono HTTP standard, quindi proxy, load balancer e CDN li gestiscono come qualsiasi altra richiesta, senza un upgrade di protocollo da configurare lungo la strada.
- **Riconnessione gratuita.** Se la connessione del visitatore cade, il browser si riconnette da solo, e l'header standard `Last-Event-ID` permette al server di riprendere lo stream dal punto in cui si era interrotto.
- **Scalabilità orizzontale.** Ogni server di replica non conserva alcuno stato sui visitatori, quindi aumentare la capacità significa aggiungere un altro server che ascolta il principale e trasmette a chi si collega.

I limiti esistono, ma qui non pesavano. Gli SSE trasportano solo testo, che per eventi in JSON è perfetto. Con HTTP/1.1 un browser apre al massimo sei connessioni per dominio, quindi più schede dello stesso sito possono esaurirle; HTTP/2 multiplexa gli stream su un'unica connessione e il limite sparisce. E se il visitatore dovesse inviare un flusso continuo di messaggi, come in una chat o in un gioco multiplayer, i WebSocket sarebbero lo strumento giusto.

## Modus Operandi

Conclusa la fase di design, rimaneva la fase di implementazione, che abbiamo affrontato nell'ordine sopra elencato.

Abbiamo iniziato sviluppando lo scraper, e ospitandolo su una VPS di dimensioni medio-piccole su Hetzner. Qui abbiamo incontrato il primo problema: i dati non erano affatto puliti e pronti ad essere direttamente salvati / inoltrati. Abbiamo riscontrato diverse incongruenze: timestamp corrotti, contenuti vuoti e così via. Data la mole di dati da leggere, scartare solamente i dati completamente illeggibili e recuperare quelli mal formattati è stato molto dispendioso in termini di tempo. Soprattutto perché, per la dashboard, la consistenza delle informazioni era il fattore chiave.

Un altro grande problema erano i rate limit. Non potevamo semplicemente "leggere i dati da blockchain", quindi abbiamo dovuto acquistare servizi che ci permettessero di farlo in modo facile ed efficiente (Infura, per esempio). Per i compiti più facili ci siamo anche affidati agli explorer di blockchain ma, anche qui, i rate limit erano particolarmente asfissianti.

Il contesto in cui lo scraper doveva lavorare era particolarmente difficile: un enorme volume di dati da recuperare (più di 3 milioni di transazioni da arricchire, per un totale di decine di milioni di chiamate), sotto rate limit molto pesanti, il tutto da fare il più velocemente possibile. Abbiamo attraversato diverse iterazioni per arrivare a un buon compromesso tra questi vincoli. Lo scraper finale è riuscito a recuperare ogni transazione in poco meno di una settimana. Gli aspetti chiave della sua implementazione sono stati la gestione degli errori con timeout incrementali, l'assicurarci che, se lo scraper fosse andato in crash, i dati sarebbero rimasti integri, e il parallelizzare il più possibile.

Fortunatamente, il resto è stato più semplice. Per esempio, i server di replica, sono stati sviluppati con pochissime righe di codice.

La parte più difficile di tutto il backend è stato lo scrivere query SQL estremamente efficienti, dato che puntavamo all'avere risposte sotto i 100 ms per le query più importanti. Questo significava che non potevamo appoggiarci a un ORM, e l'ottimizzazione delle query SQL ha finito per prendere una parte significativa di tempo. Soprattutto per tutte le chiamate che necessitavano di aggregare centinaia di migliaia, se non milioni, di transazioni.

Il frontend non è stato problematico. Con qualsiasi altro stack tecnologico, il tempo di implementazione di questa sezione sarebbe stato molto più lungo: parliamo di almeno 2 o 3 volte tanto. Fortunatamente, mentre ci avvicinavamo al rilascio dell'MVP, Next.js ha rilasciato come supporto sperimentale il caching di componenti, query e altro ancora.

## Cambiamenti: da TimescaleDB a Supabase

C'è stato un cambiamento importante rispetto alle nostre decisioni iniziali: il database. Siamo partiti con Timescale e siamo poi passati a Supabase. La ragione principale era il costo. Supabase costava meno della metà di Timescale, pur avendo un hardware migliore. E per quanto 3 milioni di transazioni siano tante, restano comunque gestibili da qualsiasi istanza di Postgres che abbia un buon hardware e avendo uno schema che faccia vasto uso di indici.

Questo ha comportato anche una grande modifica lato backend. Supabase offre query con routing in base alla zona geografica di chi le effettua, ma solo se chiamate tramite il proprio SDK. Quindi, per sfruttare al meglio le varie read-replica del database e funzioni del backend sparse in giro per il mondo, abbiamo optato per utilizzare quest'ultima.

## Tempistiche

Lo sviluppo è iniziato a novembre 2024. Lo scraper è stata la singola funzionalità che ha richiesto più tempo: due grandi release principali rilasciate in contemporanea a due importanti aggiornamenti dello schema del database.

- **Scraper V1**: rilasciato a marzo 2025
- **Scraper V2**: rilasciato a maggio 2025
- **Lancio pubblico**: 1 giugno 2025

Il frontend e tutto il resto sono stati sviluppati in parallelo.

## Team

L'intero progetto è stato realizzato prima ancora che NFW Web Studio esistesse. È stato realizzato da un singolo membro del team, mentre il design è stato realizzato esternamente.

## Qualche numero

### Lighthouse / Speed Insights

| Metrica          | Punteggio |
| ---------------- | --------- |
| Performance      | 28        |
| Accessibilità    | 100       |
| Best Practices   | 100       |
| SEO              | 100       |
| Agentic Browsing | 1/2       |

Una nota su quel punteggio lato Performance: il progetto è stato rilasciato nella primavera del 2025, sviluppato attivamente fino a novembre 2025 e tenuto in normale manutenzione fino all'inizio del 2026. Finita la manutenzione, il cliente ha lanciato una nuova dashboard con un rebrand, oggi online su [dashboard.dual.network](https://dashboard.dual.network/). La versione che abbiamo costruito è ancora online su [dashboard.blockv.io](https://dashboard.blockv.io), e sfrutta attualmente un database fortemente sottodimensionato e collocato in un'unica località. I punteggi di accessibilità, best practices e SEO riflettono come l'applicazione è stata effettivamente costruita; il dato sulla performance riflette in gran parte l'infrastruttura una volta finita la manutenzione e supporto attivi, più che l'architettura originale. Affidarsi pesantemente ai Suspense boundaries sarebbe un trucco per migliorare estremamente il punteggio duplicandolo se non triplicandolo. Anche se, per la natura del progetto, sono i dati e la loro coerenza a contare, quindi, avere dei loader senza dati, servirebbe a gran poco.

### Analisi delle query: prestazioni di Postgres

Escludendo le query dello scraper, il database racconta una storia chiara: **le query in quella che definiamo "hot path" (cammino critico) sono estremamente veloci, mentre quelle pesanti sono deliberatamente tenute fuori da esso.**

**Hot path:**

- **Feed eventi live** (`get_smartnft_events`): \~141.000 chiamate a **\~16,6 ms di media**, con un tasso di cache hit del \~99%. Questo è il feed in tempo reale che alimenta la dashboard, ed è rapido.
- **Lookup di un singolo SmartNFT** (`get_smartnft`): \~38.800 chiamate a **\~9 ms di media**, \~95% di cache hit. Le viste di dettaglio si risolvono quasi istantaneamente.
- **Listing degli indirizzi**: \~172.600 chiamate a **\~44 ms di media**, \~97% di cache hit. Comodamente entro il budget per una lista paginata.

**Aggregati pesanti (deliberatamente fuori dal percorso critico):**

Una manciata di query sono estremamente costose. Per esempio il riepilogo dei publisher (`get_smartnft_publishers_summary`, \~3,2 s di media) e il calcolo dei saldi dei wallet (`get_wallet_balances`, \~1,4 s di media, che a ogni chiamata somma su \~1,4M di trasferimenti). Questo è stato un compromesso accettabile: queste query hanno un volume molto basso di chiamate, sono estremamente ottimizzate a un livello superiore grazie al caching e non bloccano mai il rendering iniziale.

## Servizi coinvolti

Questo progetto rientra in:

[Siti & web app](https://www.nfw.run/#servizi)

Web app come questa fanno parte della nostra [realizzazione siti web](https://www.nfw.run/creazione-siti-web): i siti ad alta interattività, con login, permessi, dashboard o CMS, hanno un preventivo su misura.

## Vuoi un risultato così per la tua azienda?

[Tutti i casi studio](https://www.nfw.run/casi-studio) [Parliamone](https://www.nfw.run/contatti)
