È uscito il Corso Java Completo
Torna al blog

Cos'è Firebase e quando ha senso usarlo

Cos'è Firebase e quando conviene: Firestore, autenticazione, hosting e funzioni, il modello di prezzo a consumo e il vero costo nascosto, cioè il lock-in.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

10 min di lettura

Devi fare un'app, hai due settimane, e l'ultima cosa che vuoi è passarne una a configurare un server, un database e un sistema di login. Firebase è la risposta che il settore dà a questo problema da più di dieci anni, ed è una risposta che funziona — con un conto da pagare più avanti, che è bene conoscere prima. In questo articolo trovi cosa contiene Firebase, come Firestore ti costringe a pensare ai dati in modo diverso, come funziona davvero il prezzo e dove sta la trappola vera.

Cos'è

Firebase è la piattaforma di Google che ti dà, come servizi già pronti, tutto quello che sta dietro a un'applicazione: database, autenticazione, hosting, funzioni, notifiche e analisi, senza che tu debba gestire un solo server.

Nasce nel 2011 come servizio di database sincronizzato in tempo reale, viene acquisita da Google nel 2014 e da lì diventa una suite completa dentro Google Cloud Platform. È una delle piattaforme di questo tipo più mature in circolazione, e la maturità si vede: documentazione ampia, librerie curate per ogni piattaforma, un numero enorme di risposte già scritte per i problemi che incontrerai.

Il modello è quello del backend-as-a-service: il cloud ti vende i mattoni già montati, tu scrivi solo l'applicazione.

Firestore, e come ti cambia il modo di pensare

Firestore è il database principale di Firebase, ed è la parte che va capita prima di tutte le altre, perché condiziona ogni decisione successiva.

Non è un database relazionale: è un database NoSQL a documenti. I dati non stanno in tabelle con colonne fisse, ma in documenti in stile JSON, raccolti in collezioni. Un documento può contenere sotto-collezioni, e ogni documento della stessa collezione può avere campi diversi dagli altri.

// collezione "utenti", documento con id "u_1029"
{
  nome: "Mario",
  email: "mario@esempio.it",
  piano: "pro",
  preferenze: { tema: "scuro", newsletter: true }
}

Scrivere e leggere è immediato:

import { collection, query, where, getDocs } from "firebase/firestore";

const q = query(collection(db, "ordini"), where("utenteId", "==", "u_1029"));
const snap = await getDocs(q);
snap.forEach((d) => console.log(d.id, d.data()));

Il cambio di mentalità

Se vieni da SQL, la differenza che conta non è la sintassi. È che in Firestore non esistono i join. Non puoi scrivere una query che unisce ordini e utenti e ti restituisce il risultato aggregato. Se ti serve il nome dell'utente accanto all'ordine, hai due strade: fare una seconda lettura per ogni ordine, oppure copiare il nome dentro il documento dell'ordine.

La seconda si chiama denormalizzazione, ed è la prassi. Significa che lo stesso dato vive in più posti, e che quando cambia devi aggiornarlo ovunque sia finito. Il database non ti avvisa se te ne dimentichi uno.

Questo ribalta l'ordine con cui progetti. In un relazionale modelli i dati secondo la loro struttura logica e poi scrivi le query. In Firestore parti dalle schermate: modelli i documenti in modo che ogni vista si risolva con una lettura sola. È legittimo e spesso efficiente — ma è un modo diverso, e chi lo scopre a metà progetto perde tempo. La differenza di fondo la spiego in SQL vs NoSQL.

C'è anche il Realtime Database, il servizio originario: un unico albero JSON, più semplice ma con query molto più limitate. Per un progetto nuovo si parte da Firestore.

Authentication: il pezzo più forte

Se c'è una parte di Firebase che merita l'entusiasmo, è l'autenticazione.

Copre email e password, numero di telefono con SMS, accesso anonimo, e i provider esterni — Google, Apple, Facebook, GitHub, Microsoft — con una manciata di righe. Gestisce verifica dell'email, reimpostazione della password, aggiornamento dei token, sessioni multi-dispositivo. È il tipo di funzionalità che sembra semplice finché non provi a scriverla, e allora scopri quanti casi limite ci sono.

Molti team usano Firebase Authentication da solo, con il resto dello stack costruito altrove: database proprio, API proprie, hosting proprio. È una scelta sensata, perché è il servizio con il miglior rapporto tra quello che ti risparmia e quanto ti lega.

Hosting e Cloud Functions

Firebase Hosting serve siti statici e single-page application da una rete distribuita, con HTTPS incluso e distribuzione in un comando (firebase deploy --only hosting). È una soluzione di hosting più che adeguata per un frontend, benché oggi ci siano parecchie alternative altrettanto valide — ne parlo in come fare il deploy di un sito.

Cloud Functions sono funzioni serverless che girano su richiesta: rispondono a una chiamata HTTP, oppure si attivano quando succede qualcosa nel database o nell'autenticazione. Sono il posto dove mettere la logica che non può stare nel client — chiavi segrete, chiamate a servizi di pagamento, ricezione di un webhook.

exports.suNuovoUtente = functions.auth.user().onCreate(async (user) => {
  await db.collection("profili").doc(user.uid).set({
    email: user.email,
    creatoIl: admin.firestore.FieldValue.serverTimestamp(),
    piano: "free",
  });
});

Attorno c'è il resto: Cloud Storage per i file, Cloud Messaging per le notifiche push, Crashlytics, Remote Config, Analytics. Su mobile, questo insieme è difficile da eguagliare.

Il prezzo a consumo, e i conti a sorpresa

Firebase si paga a consumo: un piano gratuito con soglie mensili, e poi si paga per quello che usi. Firestore in particolare non si fattura sui gigabyte occupati, ma sulle operazioni: quanti documenti leggi, quanti ne scrivi, quanti ne cancelli, più il traffico in uscita.

Non ti do numeri — cambiano, e una cifra sbagliata è peggio di nessuna cifra. Il modello, però, va capito, perché è qui che nascono i problemi: a consumo significa che il costo dipende dal tuo codice, non dal tuo traffico. Due applicazioni con lo stesso numero di utenti possono avere bollette molto diverse.

I modi concreti di bruciare soldi

Leggere una collezione intera per filtrarla nel client. Ogni documento che torna è una lettura fatturata: se ne scarichi cinquemila per mostrarne dieci, hai pagato cinquemila letture.

// male: paghi ogni documento della collezione
const tutti = await getDocs(collection(db, "ordini"));
const miei = tutti.docs.filter((d) => d.data().utenteId === uid);

// bene: filtra il database, paghi solo i risultati
const miei = await getDocs(
  query(collection(db, "ordini"), where("utenteId", "==", uid))
);

Il classico problema N+1. Carichi cento ordini, e per ognuno leggi l'utente collegato: centouno letture per una schermata. È il caso in cui la denormalizzazione ti salva — il nome andava copiato dentro l'ordine.

Ascoltatori in tempo reale lasciati aperti. Un listener che non viene chiuso quando il componente sparisce continua a ricevere aggiornamenti e a generare letture. Con la navigazione avanti e indietro se ne accumulano decine. Chiudi sempre la sottoscrizione quando smonti il componente.

Un ciclo infinito in una Cloud Function. Una funzione che scrive su un documento e si attiva sulle scritture di quello stesso documento si richiama all'infinito. Ogni giro è una scrittura, un'invocazione e altre letture. È l'errore che produce le storie di bollette a quattro cifre in una notte.

Un endpoint pubblico senza limiti. Se una funzione è raggiungibile senza autenticazione, chiunque può chiamarla in ciclo, e con il consumo questo si traduce in denaro.

Due abitudini da prendere il primo giorno: imposta gli avvisi di budget su Google Cloud e usa l'emulatore locale in sviluppo, così le prove non toccano il progetto reale.

E come per ogni piattaforma di questo tipo, la sicurezza sta nelle regole lato server: le Security Rules decidono chi legge cosa. Regole permissive lasciate da quando stavi provando equivalgono a un database aperto — vedi rendere sicuro un sito.

Le abitudini che fanno la differenza sul conto:

ComportamentoEffetto sulla bollettaAlternativa
Filtrare nel clientPaghi ogni documento lettoFiltra con where nella query
Leggere l'utente per ogni rigaLetture moltiplicate (N+1)Copia il dato dentro il documento
Listener mai chiusiLetture che continuano da soleChiudi la sottoscrizione allo smontaggio
Funzione che si riattiva da séCiclo infinito, costo esplosivoFiltra il trigger, o scrivi altrove
Endpoint senza autenticazioneChiamabile in ciclo da chiunqueSecurity Rules e controllo di accesso

Il costo vero: il lock-in

I soldi sono un problema gestibile: si ottimizzano le query e si torna sotto controllo. Il costo che non si risolve con un'ottimizzazione è un altro.

Firestore è un database proprietario. Non esiste una versione che puoi installare su una macchina tua, non c'è un formato standard da cui ripartire, non c'è un altro fornitore che offra lo stesso servizio. Puoi esportare i dati, certo — ma esportare i dati è la parte facile.

La parte difficile è che il tuo codice si è modellato attorno a Firestore. Le query usano le sue API. La struttura dei documenti riflette i suoi limiti: denormalizzata, senza relazioni, progettata sulle schermate. La sicurezza vive nelle Security Rules, in un linguaggio che esiste solo lì. Migrare non significa spostare dei dati: significa riprogettare il modello e riscrivere il livello di accesso, con un'applicazione in produzione da tenere in piedi nel frattempo.

Non lo dico per spaventare, ma perché è una scelta da fare consapevolmente, non da scoprire dopo. Se il progetto è un prototipo, il lock-in non ti costerà mai nulla. Se è il prodotto su cui costruisci un'azienda per dieci anni, è una variabile da mettere sul tavolo il primo giorno.

Quando è la scelta giusta

Sì, senza esitazione, quando:

  • Stai facendo un'app mobile, nativa o con Flutter. L'integrazione è la migliore del settore, e la sincronizzazione offline è una funzionalità che da solo non costruisci.
  • Stai facendo un MVP o un prototipo e conta arrivare in fondo. Vedi cos'è un MVP.
  • Il team è piccolo e nessuno vuole occuparsi di infrastruttura.
  • Ti serve solo l'autenticazione: prendi quella, lascia il resto.
  • L'app è per sua natura in tempo reale e collaborativa.

Meglio valutare altro quando:

  • I dati sono fortemente relazionali, con report e query che incrociano più entità. Firestore lì lavora controcorrente — quale database scegliere inquadra la decisione.
  • Ti serve un costo prevedibile mese su mese.
  • La portabilità è un requisito, per strategia o per vincoli di conformità.
  • Ti servono aggregazioni e analisi sui dati: dovrai comunque esportarli altrove.

In sintesi

Firebase è una piattaforma matura e ben fatta, e la sua maturità è un vantaggio concreto: le cose funzionano e le risposte esistono già.

Firestore non è un database relazionale, ed è la scelta che determina tutto il resto: niente join, denormalizzazione come prassi, modellazione a partire dalle schermate.

Il prezzo è a consumo e dipende dalle tue query, non dai tuoi utenti: query mal scritte, listener non chiusi e cicli nelle funzioni sono i modi concreti in cui arriva una bolletta a sorpresa.

Il costo vero è il lock-in. Uscire da Firestore significa riscrivere, non esportare. È accettabile — a patto di saperlo prima.

Il confronto diretto con l'alternativa open source è in Supabase o Firebase; per capire l'altro approccio, cos'è Supabase.