Cos'è ElectricSQL
ElectricSQL è una piattaforma open-source che sincronizza PostgreSQL con applicazioni client in real-time, creando un'esperienza local-first senza sacrificare la coerenza dei dati. A differenza degli approcci tradizionali client-server dove il database è una "fonte di verità remota", ElectricSQL permette ai client di avere una copia locale completa dei dati di loro interesse, operare offline senza dipendere dalla rete, e sincronizzarsi automaticamente con il server quando la connessione è disponibile. È costruito su PostgreSQL (non sostituisce), aggiungendo uno strato di sincronizzazione che gestisce conflitti, ordinamento cauzale e convergenza finale tra tutte le copie.
💡 Caratteristiche fondamentali
- Sincronizzazione real-time: Push e pull bidirezionali tra client e server
- Operatività offline: Lavora anche senza connessione, sync automatica al ripristino
- Gestione conflitti: Risoluzione automatica con CRDTs e causalità logica
- PostgreSQL nativo: Usa il database che già conosci, niente nuove tecnologie esotiche
- Schema-driven: Definisci con SQL standard, il resto è automatico
Architettura di sincronizzazione
ElectricSQL si divide in tre componenti chiave: il server di sincronizzazione (Electric Server), il client JavaScript/TypeScript (ElectricClient), e il database PostgreSQL. Il flusso di sincronizzazione funziona così: quando il client modifica un record localmente, ElectricClient lo registra in una coda di sync. Periodicamente (o immediatamente se la rete è disponibile), la coda viene inviata a Electric Server. Il server applica le modifiche a PostgreSQL, verifica i conflitti, e rifioglia il risultato verso tutti i client interessati. I client ricevono gli aggiornamenti e aggiornano il database locale (SQLite o OPFS) in transazione ACID.
// Schema PostgreSQL (versione di verità)
CREATE TABLE items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
completed BOOLEAN DEFAULT false,
userId UUID NOT NULL REFERENCES users(id),
createdAt TIMESTAMPTZ DEFAULT now(),
updatedAt TIMESTAMPTZ DEFAULT now()
);
// Client TypeScript - Definisci lo schema locale
import {{ '{' }} ElectricDatabase {{ '}' }} from 'electric-sql';
const db = new ElectricDatabase('items_db');
// Sincronizza solo i record del current user
const {{ '{' }} items {{ '}' }} = await db.syncTable('items', {{ '{' }}
where: `userId = '${{ '{' }}currentUserId{{ '}' }}'`
{{ '}' }});
// Crea un item localmente
const newItem = {{ '{' }}
title: 'Implementare ElectricSQL',
completed: false,
userId: currentUserId
{{ '}' }};
await db.items.create(newItem);
// ✅ Disponibile SUBITO localmente, offline se necessario
Modello di dati locale
ElectricSQL sincronizza i dati su un database locale SQLite (browser/mobile) o OPFS (Origin Private File System) per applicazioni web. Questo database locale è una replica parziale e opportunistica del server: contiene solo i dati "abbonati" dal client. Grazie alla sincronizzazione selettiva (filtering con WHERE clause), puoi sincronizzare solo i record rilevanti—una todo-list personale per 1000 utenti non scaricherà le 1000 liste intere, ma solo quella dell'utente corrente.
// Sincronizza solo i post del blog personale
const {{ '{' }} posts {{ '}' }} = await db.syncTable('posts', {{ '{' }}
where: `authorId = '${{ '{' }}userId{{ '}' }}'`
{{ '}' }});
// Live query locale
const allPosts$ = posts.live();
allPosts$.subscribe(list => {{ '{' }}
console.log('Post aggiornati:', list); // Re-render UI
{{ '}' }});
// Scrive offline
posts.create({{ '{' }}
title: 'ElectricSQL Rocks',
content: 'Lorem ipsum...',
authorId: userId
{{ '}' }});
// ✅ Applicato ISTANTANEAMENTE nel database locale
// ⏳ Sync in background quando online
Gestione automatica dei conflitti
Uno dei problemi più spinosi nel local-first è cosa succede quando due dispositivi modificano lo stesso record offline. ElectricSQL affronta questo con CRDTs (Conflict-free Replicated Data Types) e ordinamento causale. Ogni operazione riceve un timestamp logico unico, e il server tiene traccia della causalità tra operazioni. Se due client modificano lo stesso campo, ElectricSQL applica una strategia di risoluzione (Last-Write-Wins di default, personalizzabile) e tutti i client convergono allo stesso stato finale senza perdere dati.
// Client A (offline): modifica l'item
const item = await db.items.findUnique(itemId);
item.title = 'Task A - Online offline'; // Client A modifica
item.status = 'in_progress';
await db.items.update({{ '{' }}
where: {{ '{' }} id: itemId {{ '}' }},
data: item
{{ '}' }});
// Client B (online): modifica LO STESSO item
// (scena simultanea)
await fetch('/api/items/' + itemId, {{ '{' }}
method: 'PATCH',
body: JSON.stringify({{ '{' }}
title: 'Task A - Online only', // CONFLITTO!
status: 'completed'
{{ '}' }})
{{ '}' }});
// Risultato: ElectricSQL CONVERGE automaticamente
// Strategia: Last-Write-Wins + merge campi
// title: 'Task A - Online only' (client B ha scritto dopo)
// status: 'completed' (tutti concordano)
// ✅ Zero data loss, zero conflitti visibili
Integrazione con le applicazioni
ElectricSQL fornisce client librerie per TypeScript, React, React Native e Svelte. L'integrazione è declarativa: usi un hook (React) o uno store (Svelte) per sottoscriverti ai cambiamenti dei dati, e il UI si aggiorna automaticamente. Non devi gestire fetch, caching, invalidazione— è tutto baked-in.
import {{ '{' }} useElectric {{ '}' }} from 'electric-sql/react';
import {{ '{' }} useState {{ '}' }} from 'react';
export function TodoList() {{ '{' }}
const db = useElectric();
const {{ '{' }} items {{ '}' }} = db.useQuery(
db.items.findMany({{ '{' }} where: {{ '{' }} completed: false {{ '}' }} {{ '}' }})
);
const [title, setTitle] = useState('');
const handleAdd = async () => {{ '{' }}
await db.items.create({{ '{' }} title, completed: false {{ '}' }});
setTitle('');
// ✅ UI re-renders AUTOMATICAMENTE (live reactive query)
{{ '}' }};
return (
<div>
<input
value={{ '{' }}title{{ '}' }}
onChange={{ '{' }}(e) => setTitle(e.target.value){{ '}' }}
/>
<button onClick={{ '{' }}handleAdd{{ '}' }}>Aggiungi</button>
<ul>
{{ '{' }}items?.map(item => (
<li key={{ '{' }}item.id{{ '}' }}>{{ '{' }}item.title{{ '}' }}</li>
)){{ '}' }}
</ul>
</div>
);
{{ '}' }}
Performance e scalabilità
ElectricSQL è disegnato per scale a migliaia di client simultanei. Ogni client sincronizza solo i dati che gli servono (selective replication), il che riduce drasticamente banda di rete e storage locale. Il server utilizza PostgreSQL nativo (non replica speciale), quindi tutti gli indici, trigger e procedimenti standard SQL funzionano. Le performance di lettura locale (SQLite) sono nell'ordine dei microsecondi—centinaia di volte più veloci di una chiamata API remotale.
Confronto con altre soluzioni local-first
Altre piattaforme come Firestore, Realm, e CouchDB offrono sincronizzazione, ma hanno tradeoff diversi. Firestore è cloud-only e non standard SQL. Realm è un database mobile specializzato, non web-first. CouchDB è un documento store non relazionale. ElectricSQL si distingue per mantenere PostgreSQL come fonte di verità, permettendo di usare query SQL standard e l'intero ecosistema PostgreSQL (extension, full-text search, JSON, geospatial, ecc.). Non sei bloccato in un vendor proprietario.
Casi d'uso ideali
ElectricSQL brilla in scenari dove:
✅ Applicazioni offline-first — app di note, todo, calendario che funzionano perfettamente senza rete.
✅ Bassa latenza critica — le scritte sono istantanee nel database locale, non aspetti roundtrip al server.
✅ Banda limitata — mobile con 2G/3G, o ambienti dove la larghezza di banda è costosa.
✅ Collaborazione real-time — più utenti modificano gli stessi dati, ElectricSQL gestisce i conflitti.
✅ Applicazioni ibrida online/offline — passa da online a offline senza cambiare codice.
Best practice di implementazione
Quando usi ElectricSQL, considera questi principi:
1. Sincronizza selettivamente — Non abbonati a interi tabelle. Usa WHERE per filtrare solo i record rilevanti.
2. Comprendi la causalità — ElectricSQL preserva l'ordinamento causale, ma non l'ordinamento globale.
Se A modifica X, poi B modifica Y, localmente potresti vedere Y prima di X se la latenza di rete varia.
3. Gestisci gli errori di sincronizzazione — La rete fallisce. Scrivi UI che comunica quando offline,
e che riprova automaticamente.
4. Usa transazioni locali — ElectricSQL supporta transazioni ACID nel database locale.
Raggruppale le operazioni correlate.
Limitazioni e considerazioni
ElectricSQL non è una soluzione universale. Limitazioni note:
❌ Richiede schema relazionale — Non funziona bene con dati gerarchici complessi o documenti annidati.
❌ Sincronizzazione opportunistica — Se il client rimane offline per giorni, il sync potrebbe essere lento al ripristino.
❌ Non supporta query join remoti — Sincronizzi tabelle, non risultati di join. Devi fare join localmente.
❌ Gestione memoria — Un database locale grande occupa memoria. Considera pagine o lazy loading.
Setup iniziale e configurazione
Partire con ElectricSQL è semplice. Installa il client npm, configura il server (self-hosted o cloud), e definirai lo schema con SQL standard.
# Installa il client TypeScript
npm install electric-sql
# Configura il server (Docker)
docker run -e DATABASE_URL=postgres://user:pass@postgres:5432/app \
-e ELECTRIC_ALLOW_DIRTY_DEFAULT=true \
electricsql/electric:latest
# Nel tuo progetto TypeScript
import {{ '{' }} ElectricDatabase {{ '}' }} from 'electric-sql';
const db = new ElectricDatabase('myapp.db');
await db.connect({{ '{' }}
host: 'localhost',
port: 5133,
database: 'myapp'
{{ '}' }});
// ✅ Pronto a sincronizzare
Conclusione
ElectricSQL rappresenta un approccio maturo e pragmatico al local-first: mantiene PostgreSQL come fondamento (puoi usare tutto l'ecosistema SQL), sincronizza in real-time, risolve conflitti automaticamente, e permette ai client di operare offline senza compromessi sull'esperienza utente. Non è una soluzione universale (schema relazionale, sync opportunistica), ma per le applicazioni che rientrano nei suoi casi d'uso (offline-first, bassa latenza, banda limitata, collaborazione), è incredibilmente efficace. Se costruisci un'app moderna dove l'esperienza offline è critica e la coerenza dati è non-negoziabile, ElectricSQL merita di essere nella tua cassetta degli attrezzi.
🎯 Punti chiave
- ElectricSQL sincronizza PostgreSQL con client locale in real-time
- Operatività offline completa + sync automatica al ripristino
- CRDTs e causalità logica per risoluzione conflitti zero-touch
- Sincronizzazione selettiva: scarica solo dati rilevanti
- Integrazione semplice con React, TypeScript, React Native
- Ideale per offline-first, bassa latenza, collaborazione real-time
Domande frequenti
ElectricSQL richiede un database PostgreSQL dedicato?
Sì, ElectricSQL necessita di un server PostgreSQL. Non è un database nuovo, ma uno strato di sincronizzazione che si basa su PostgreSQL come fonte di verità. Puoi usare PostgreSQL self-hosted o cloud (Render, Railway, Supabase).
Cosa succede se due client modificano lo stesso record offline?
ElectricSQL usa CRDTs e ordinamento causale per convergere automaticamente. Applica una strategia di risoluzione (Last-Write-Wins di default), assicurando che tutti i client vedono lo stesso risultato finale senza conflitti visibili.
ElectricSQL funziona con SQLite nel browser?
Sì, ElectricSQL usa SQLite per il database locale in browser (via OPFS o emscripten). Offre ACID transazioni locali e performance di lettura nel range dei microsecondi.
Posso usare ElectricSQL con un'app React Native?
Sì, ElectricSQL supporta React Native. Il database locale usa SQLite nativo (via react-native-sqlite), e l'integrazione è con hook React standard.
Qual è la quantità di dati che ElectricSQL può sincronizzare?
ElectricSQL usa sincronizzazione selettiva, per cui sincronizzi solo i dati rilevanti per il client. Un'applicazione mobile con 1000 utenti non scaricherà l'intero database, ma solo i record dell'utente corrente. Lo storage locale è limitato da SQLite/browser (tipicamente GB)

Commenti
Caricamento commenti...
Accedi per commentare