Saltar al contenido principal

Firebase

Plataforma de Google que incluye base de datos NoSQL en tiempo real, autenticación, hosting, funciones y más. Ideal para apps móviles y web con sincronización en tiempo real.

Límites del plan gratuito (Spark)

ServicioLímite gratuito
Firestore storage1 GB
Firestore reads50,000/día
Firestore writes20,000/día
Firestore deletes20,000/día
Realtime Database storage1 GB
Realtime Database transferencia10 GB/mes
Hosting storage10 GB
Hosting transferencia360 MB/día
AuthenticationIlimitado (gratis)
Cloud Functions2,000,000 invocaciones/mes
Storage (Files)5 GB, 1 GB/día descarga
ProyectosIlimitados

Crear proyecto Firebase

1. https://console.firebase.google.com
2. Create a project
3. Nombre del proyecto
4. Google Analytics: habilitar o no (opcional para dev)
5. Crear proyecto

Configurar Firestore

Firebase Console → Build → Firestore Database → Create database
→ Start in test mode (para desarrollo rápido)
→ Región: elegir la más cercana
aviso

El "test mode" permite read/write sin autenticación. Cambiar las reglas antes de ir a producción.

Instalar Firebase SDK

npm install firebase
# Para backend/admin:
npm install firebase-admin

Inicializar Firebase (cliente/frontend)

// lib/firebase.ts
import { initializeApp, getApps } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'
import { getAuth } from 'firebase/auth'
import { getStorage } from 'firebase/storage'

const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
}

const app = getApps().length === 0
? initializeApp(firebaseConfig)
: getApps()[0]

export const db = getFirestore(app)
export const auth = getAuth(app)
export const storage = getStorage(app)

Obtener las credenciales en:

Firebase Console → Project Settings → Your apps → Web app → Config

Operaciones CRUD con Firestore

import { 
collection, doc, addDoc, getDoc, getDocs,
updateDoc, deleteDoc, query, where, orderBy, limit,
serverTimestamp, onSnapshot
} from 'firebase/firestore'
import { db } from '@/lib/firebase'

// CREATE (auto-ID)
const docRef = await addDoc(collection(db, 'usuarios'), {
nombre: 'Juan',
email: 'juan@email.com',
activo: true,
createdAt: serverTimestamp()
})
console.log('ID creado:', docRef.id)

// CREATE (ID manual)
await setDoc(doc(db, 'usuarios', 'mi-id-custom'), {
nombre: 'Pedro',
email: 'pedro@email.com'
})

// READ (un documento)
const docSnap = await getDoc(doc(db, 'usuarios', userId))
if (docSnap.exists()) {
console.log(docSnap.data())
}

// READ (colección)
const querySnapshot = await getDocs(collection(db, 'usuarios'))
querySnapshot.forEach(doc => {
console.log(doc.id, doc.data())
})

// READ con filtros
const q = query(
collection(db, 'usuarios'),
where('activo', '==', true),
orderBy('createdAt', 'desc'),
limit(10)
)
const snapshot = await getDocs(q)

// UPDATE (solo campos específicos)
await updateDoc(doc(db, 'usuarios', userId), {
nombre: 'Juan Carlos',
updatedAt: serverTimestamp()
})

// DELETE
await deleteDoc(doc(db, 'usuarios', userId))

Realtime (onSnapshot)

import { onSnapshot, collection, query, where, orderBy } from 'firebase/firestore'

// Escuchar cambios en tiempo real
const q = query(
collection(db, 'mensajes'),
orderBy('timestamp', 'desc'),
limit(50)
)

const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach(change => {
if (change.type === 'added') {
console.log('Nuevo mensaje:', change.doc.data())
}
if (change.type === 'modified') {
console.log('Mensaje editado:', change.doc.data())
}
if (change.type === 'removed') {
console.log('Mensaje eliminado:', change.doc.id)
}
})
})

// Detener escucha (importante para evitar memory leaks)
unsubscribe()

Autenticación

import { 
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
GoogleAuthProvider,
signOut,
onAuthStateChanged
} from 'firebase/auth'
import { auth } from '@/lib/firebase'

// Registro
const { user } = await createUserWithEmailAndPassword(
auth, email, password
)

// Login
const { user } = await signInWithEmailAndPassword(
auth, email, password
)

// Login con Google
const provider = new GoogleAuthProvider()
const { user } = await signInWithPopup(auth, provider)

// Logout
await signOut(auth)

// Observer del estado de sesión
onAuthStateChanged(auth, (user) => {
if (user) {
console.log('Usuario logueado:', user.uid, user.email)
} else {
console.log('No hay sesión activa')
}
})

Reglas de seguridad de Firestore

// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {

// Solo el dueño puede leer/escribir sus datos
match /usuarios/{userId} {
allow read, write: if request.auth != null
&& request.auth.uid == userId;
}

// Posts: lectura pública, escritura autenticada
match /posts/{postId} {
allow read: if true;
allow create: if request.auth != null;
allow update, delete: if request.auth != null
&& request.auth.uid == resource.data.autorId;
}

// Admin solo
match /admin/{document=**} {
allow read, write: if request.auth != null
&& request.auth.token.admin == true;
}
}
}

Subcolecciones (datos relacionados)

// Estructura: usuarios/{userId}/posts/{postId}

// Agregar post a un usuario específico
await addDoc(
collection(db, 'usuarios', userId, 'posts'),
{ titulo: 'Mi post', contenido: '...' }
)

// Leer posts de un usuario
const postsRef = collection(db, 'usuarios', userId, 'posts')
const posts = await getDocs(postsRef)

Firebase Admin (backend)

// lib/firebase-admin.ts
import admin from 'firebase-admin'

if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
})
})
}

export const adminDb = admin.firestore()
export const adminAuth = admin.auth()

⚠️ Limitaciones importantes

  • 50k reads/día: se puede agotar con dashboards o listas que leen muchos documentos
  • Sin JOINs ni queries complejas: Firestore no soporta queries SQL; el modelo de datos debe diseñarse para las queries que necesitas
  • Sin agregaciones nativas: para contar, sumar, etc. debes usar Cloud Functions o mantener contadores manuales
  • Cold starts en Functions: las Cloud Functions free tier tienen cold start alto