DEV Community

Cover image for Cómo Usar la API de Firebase: Guía Completa de Integración (2026)
Roobia
Roobia

Posted on • Originally published at apidog.com

Cómo Usar la API de Firebase: Guía Completa de Integración (2026)

Estás construyendo una aplicación. Los usuarios necesitan iniciar sesión. Los datos deben sincronizarse en tiempo real. Los archivos necesitan almacenamiento. Podrías levantar servidores, configurar bases de datos y gestionar la infraestructura durante semanas. O podrías usar Firebase.

Prueba Apidog hoy

Firebase potencia más de 1.5 millones de aplicaciones, incluyendo The New York Times, Duolingo y Alibaba. Los desarrolladores lo eligen porque elimina la complejidad del backend. Te enfocas en las características, no en el mantenimiento del servidor. Pero la API de Firebase tiene sus peculiaridades. Los flujos de autenticación confunden a los principiantes. Las reglas de la base de datos complican a los desarrolladores experimentados. Las Cloud Functions parecen mágicas hasta que entiendes los activadores.

He integrado Firebase en aplicaciones de producción que atienden a millones de usuarios. He cometido todos los errores posibles: expuse claves de cuentas de servicio, escribí consultas ineficientes, implementé funciones defectuosas. Esta guía destila esas lecciones.

Aprenderás autenticación, operaciones de base de datos, Cloud Functions y almacenamiento. Verás código funcional, no solo teoría. Evitarás los problemas que causan incendios en producción.

💡Probar las API de Firebase se vuelve más fácil con herramientas adecuadas de cliente API. Apidog te permite organizar puntos finales, probar flujos de autenticación y compartir colecciones con tu equipo. Mostraremos dónde encaja naturalmente en el flujo de trabajo.

¿Qué es la API de Firebase y por qué es importante?

Firebase no es una única API. Es un conjunto de servicios de backend a los que accedes vía SDKs unificados y puntos finales REST.

Servicios Principales de Firebase

Servicio Propósito Tipo de API
Authentication Inicio de sesión e identidad de usuario SDK + REST
Firestore Database Base de datos de documentos NoSQL SDK + REST
Realtime Database Sincronización en tiempo real de JSON SDK + REST
Cloud Storage Almacenamiento de archivos y CDN SDK + REST
Cloud Functions Cómputo sin servidor CLI de despliegue
Hosting Alojamiento web estático CLI de despliegue
Cloud Messaging Notificaciones push HTTP v1 API

Cuándo Firebase Tiene Sentido

Usa Firebase si:

  • Necesitas sincronización en tiempo real (chat, colaboración, actualizaciones en vivo)
  • Quieres arquitectura sin servidor (sin gestión de infraestructura)
  • Estás construyendo apps móviles o web (los SDKs manejan las diferencias)
  • Requieres soporte sin conexión (SDKs cachean datos)
  • Quieres autenticación integrada (Google, Apple, email, teléfono)

Evita Firebase si:

  • Necesitas consultas relacionales complejas (opta por PostgreSQL)
  • Tienes requisitos estrictos de residencia de datos (regiones limitadas)
  • Necesitas SQL completo (Firestore tiene restricciones)
  • El costo a escala importa más que la velocidad de desarrollo (autoalojamiento es más barato)

La Arquitectura de la API de Firebase

Firebase utiliza un enfoque híbrido:

┌─────────────────────────────────────────────────────────┐
│                    Tu Aplicación                         │
├─────────────────────────────────────────────────────────┤
│  Firebase SDK (Cliente)                                  │
│  - Maneja automáticamente los tokens de autenticación    │
│  - Gestiona la caché sin conexión                        │
│  - Escuchas en tiempo real                               │
└─────────────────────────────────────────────────────────┘
                          │
                          │ HTTPS + WebSocket
                          ▼
┌─────────────────────────────────────────────────────────┐
│                   Backend de Firebase                    │
├──────────────┬──────────────┬──────────────┬────────────┤
│   Servicio   │   Base de    │   Servicio   │  Tiempo de │
│  de Autent.  │    Datos     │  de Almac.   │  Ejecución │
│              │   Firestore  │              │  de Func.  │
└──────────────┴──────────────┴──────────────┴────────────┘
Enter fullscreen mode Exit fullscreen mode

Los SDK de cliente abstraen la capa HTTP. Cada operación es una llamada API REST autenticada con JWT.

Autenticación de Firebase: Configuración Completa

La autenticación es la integración esencial. Mal implementada, nada más funciona.

Paso 1: Crear Proyecto de Firebase

  1. Ve a la Consola de Firebase
  2. Haz clic en "Agregar proyecto", elige un nombre (sin espacios)
  3. Habilita Google Analytics (opcional pero recomendado)
  4. Haz clic en "Crear proyecto" y espera el aprovisionamiento

Paso 2: Registrar Tu Aplicación

Web:

// En la Consola de Firebase > Configuración del Proyecto > General
// Haz clic en "Agregar aplicación" > Icono web

const firebaseConfig = {
  apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  authDomain: "tu-app.firebaseapp.com",
  projectId: "tu-app",
  storageBucket: "tu-app.appspot.com",
  messagingSenderId: "123456789012",
  appId: "1:123456789012:web:abc123def456"
};

import { initializeApp } from 'firebase/app';
const app = initializeApp(firebaseConfig);
Enter fullscreen mode Exit fullscreen mode

iOS:

Descarga GoogleService-Info.plist y agrégalo al proyecto Xcode.

Android:

Descarga google-services.json y colócalo en app/. Añade a build.gradle:

// build.gradle a nivel de proyecto
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.0'
    }
}

// build.gradle a nivel de aplicación
plugins {
    id 'com.google.gms.google-services'
}
Enter fullscreen mode Exit fullscreen mode

Paso 3: Habilitar Métodos de Autenticación

En la Consola de Firebase > Autenticación > Método de inicio de sesión:

  1. Correo electrónico/Contraseña: Habilítalo para registro tradicional
  2. Google: Agrega SHA-1 (Android) o ID de paquete (iOS)
  3. Apple: Obligatorio en iOS si usas sociales
  4. Teléfono: Habilita para SMS (requiere facturación)

Paso 4: Implementar Flujo de Autenticación

Registro con correo/contraseña:

import {
  createUserWithEmailAndPassword,
  getAuth,
  updateProfile
} from 'firebase/auth';

const auth = getAuth(app);

async function signUp(email, password, displayName) {
  try {
    const userCredential = await createUserWithEmailAndPassword(
      auth,
      email,
      password
    );
    await updateProfile(userCredential.user, { displayName });
    console.log('Usuario creado:', userCredential.user.uid);
    return userCredential.user;
  } catch (error) {
    switch (error.code) {
      case 'auth/email-already-in-use':
        throw new Error('Este correo electrónico ya está registrado');
      case 'auth/weak-password':
        throw new Error('La contraseña debe tener al menos 6 caracteres');
      case 'auth/invalid-email':
        throw new Error('Dirección de correo electrónico inválida');
      default:
        throw new Error('Fallo al registrarse: ' + error.message);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Inicio de sesión con correo/contraseña:

import {
  signInWithEmailAndPassword,
  signOut
} from 'firebase/auth';

async function signIn(email, password) {
  try {
    const userCredential = await signInWithEmailAndPassword(
      auth,
      email,
      password
    );
    const user = userCredential.user;
    const idToken = await user.getIdToken();
    console.log('Token de autenticación:', idToken);
    return user;
  } catch (error) {
    switch (error.code) {
      case 'auth/user-not-found':
        throw new Error('No existe cuenta con este correo electrónico');
      case 'auth/wrong-password':
        throw new Error('Contraseña incorrecta');
      case 'auth/too-many-requests':
        throw new Error('Demasiados intentos. Inténtalo de nuevo más tarde');
      default:
        throw new Error('Fallo al iniciar sesión');
    }
  }
}

async function logOut() {
  await signOut(auth);
  console.log('Usuario cerró sesión');
}
Enter fullscreen mode Exit fullscreen mode

Inicio de sesión con Google (Web):

import {
  GoogleAuthProvider,
  signInWithPopup
} from 'firebase/auth';

async function signInWithGoogle() {
  const provider = new GoogleAuthProvider();
  provider.addScope('email');
  provider.addScope('profile');
  try {
    const result = await signInWithPopup(auth, provider);
    const user = result.user;
    const credential = GoogleAuthProvider.credentialFromResult(result);
    const googleAccessToken = credential.accessToken;
    return user;
  } catch (error) {
    if (error.code === 'auth/popup-closed-by-user') {
      throw new Error('Inicio de sesión cancelado');
    }
    throw new Error('Fallo en el inicio de sesión con Google');
  }
}
Enter fullscreen mode Exit fullscreen mode

Paso 5: Proteger Rutas con el Estado de Autenticación

import { onAuthStateChanged } from 'firebase/auth';

onAuthStateChanged(auth, (user) => {
  if (user) {
    console.log('Usuario:', user.email);
    window.location.href = '/dashboard';
  } else {
    console.log('Ningún usuario');
    window.location.href = '/login';
  }
});
Enter fullscreen mode Exit fullscreen mode

Errores Comunes de Autenticación

Error 1: No manejar la actualización del token

Los tokens expiran tras 1 hora. No los caches en el backend; verifica en cada solicitud.

Error 2: Exponer credenciales de administrador en el cliente

Jamás uses claves de cuenta de servicio en frontend. Solo en servidores confiables.

Error 3: Omitir verificación de correo electrónico

import { sendEmailVerification } from 'firebase/auth';

async function sendVerificationEmail(user) {
  await sendEmailVerification(user);
  console.log('Correo electrónico de verificación enviado');
}

if (!auth.currentUser.emailVerified) {
  console.log('Correo electrónico no verificado');
  // Restringir acceso
}
Enter fullscreen mode Exit fullscreen mode

Base de Datos Firestore: Operaciones y Consultas

Firestore es la base de datos NoSQL de Firebase. Estructura: colecciones → documentos → subcolecciones.

Estructura de Datos

tu-proyecto (raíz)
└── users (colección)
    ├── userId123 (documento)
    │   ├── name: "John"
    │   ├── email: "john@example.com"
    │   └── posts (subcolección)
    │       ├── postId1 (documento)
    │       └── postId2 (documento)
    └── userId456 (documento)
Enter fullscreen mode Exit fullscreen mode

Inicializar Firestore

import { getFirestore } from 'firebase/firestore';

const db = getFirestore(app);
Enter fullscreen mode Exit fullscreen mode

Crear Documentos

import {
  collection,
  addDoc,
  setDoc,
  doc
} from 'firebase/firestore';

// ID autogenerado
async function createUser(userData) {
  const docRef = await addDoc(collection(db, 'users'), userData);
  console.log('Documento escrito con ID:', docRef.id);
  return docRef.id;
}

// ID personalizado
async function createUserWithId(userId, userData) {
  await setDoc(doc(db, 'users', userId), userData);
  console.log('Documento escrito con ID personalizado:', userId);
}

// Uso
const userId = await createUser({
  name: 'Alice',
  email: 'alice@example.com',
  createdAt: new Date(),
  role: 'user'
});
Enter fullscreen mode Exit fullscreen mode

Leer Documentos

import {
  getDoc,
  getDocs,
  query,
  where,
  orderBy,
  limit
} from 'firebase/firestore';

// Un documento
async function getUser(userId) {
  const docRef = doc(db, 'users', userId);
  const docSnap = await getDoc(docRef);
  if (docSnap.exists()) return docSnap.data();
  throw new Error('Usuario no encontrado');
}

// Consulta filtrada
async function getUsersByRole(role) {
  const q = query(
    collection(db, 'users'),
    where('role', '==', role),
    orderBy('createdAt', 'desc'),
    limit(10)
  );
  const querySnapshot = await getDocs(q);
  const users = [];
  querySnapshot.forEach((doc) => {
    users.push({ id: doc.id, ...doc.data() });
  });
  return users;
}

// Uso
const adminUsers = await getUsersByRole('admin');
console.log('Usuarios administradores:', adminUsers);
Enter fullscreen mode Exit fullscreen mode

Actualizar Documentos

import {
  updateDoc,
  increment,
  arrayUnion,
  arrayRemove
} from 'firebase/firestore';

async function updateUser(userId, updates) {
  const userRef = doc(db, 'users', userId);
  await updateDoc(userRef, updates);
}

// Operaciones atómicas
await updateUser('userId123', {
  loginCount: increment(1),
  tags: arrayUnion('premium', 'beta-tester'),
  lastLogin: new Date()
});

// Eliminar de una matriz
await updateUser('userId123', {
  tags: arrayRemove('beta-tester')
});
Enter fullscreen mode Exit fullscreen mode

Eliminar Documentos

import { deleteDoc } from 'firebase/firestore';

async function deleteUser(userId) {
  await deleteDoc(doc(db, 'users', userId));
  console.log('Usuario eliminado');
}
Enter fullscreen mode Exit fullscreen mode

Escuchas en Tiempo Real

import { onSnapshot } from 'firebase/firestore';

// Documento individual
const unsubscribe = onSnapshot(
  doc(db, 'users', userId),
  (doc) => {
    console.log('Usuario actualizado:', doc.data());
  },
  (error) => {
    console.error('Error de escucha:', error);
  }
);

// Consulta
const q = query(collection(db, 'posts'), where('published', '==', true));
const unsubscribeQuery = onSnapshot(q, (snapshot) => {
  const posts = snapshot.docs.map(doc => ({
    id: doc.id,
    ...doc.data()
  }));
  console.log('Publicaciones publicadas:', posts);
});

// Dejar de escuchar
unsubscribe();
unsubscribeQuery();
Enter fullscreen mode Exit fullscreen mode

Reglas de Seguridad de Firestore

Configura reglas para proteger datos en la Consola de Firebase > Firestore > Reglas:

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

    function isAuthenticated() {
      return request.auth != null;
    }

    function isOwner(userId) {
      return request.auth.uid == userId;
    }

    match /users/{userId} {
      allow read: if isAuthenticated();
      allow create: if isAuthenticated() && isOwner(userId);
      allow update, delete: if isOwner(userId);
    }

    match /posts/{postId} {
      allow read: if true; // Lectura pública
      allow create: if isAuthenticated();
      allow update, delete: if resource.data.authorId == request.auth.uid;
    }

    match /users/{userId}/private/{document} {
      allow read, write: if isOwner(userId);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Limitaciones de Consulta

Firestore limita:

  • No consultas OR (usa in o combina en cliente)
  • No búsquedas wildcard (usa Algolia/Meilisearch)
  • Consultas compuestas requieren índices
  • Límite de 30 disyunciones en in

Ejemplo de consulta OR en cliente:

const activeQuery = query(
  collection(db, 'tasks'),
  where('status', '==', 'active')
);

const pendingQuery = query(
  collection(db, 'tasks'),
  where('status', '==', 'pending')
);

const [activeSnap, pendingSnap] = await Promise.all([
  getDocs(activeQuery),
  getDocs(pendingQuery)
]);
// Fusionar resultados en el cliente
Enter fullscreen mode Exit fullscreen mode

Cloud Functions: Lógica de Backend sin Servidor

Cloud Functions ejecuta lógica backend sin servidores. Útil para activadores de base de datos, HTTP y tareas programadas.

Configuración

npm install -g firebase-tools
firebase login
firebase init functions
# Selecciona: JavaScript, ESLint sí, Express.js no
Enter fullscreen mode Exit fullscreen mode

Funciones HTTP (Puntos Finales de API)

// functions/index.js
const { onRequest } = require('firebase-functions/v2/https');
const admin = require('firebase-admin');

admin.initializeApp();
const db = admin.firestore();

// Público
exports.getPublicData = onRequest(async (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  try {
    const snapshot = await db.collection('public').get();
    const data = snapshot.docs.map(doc => doc.data());
    res.json({ success: true, data });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Protegido (token JWT)
exports.getUserProfile = onRequest(async (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  const authHeader = req.headers.authorization || '';
  const token = authHeader.split('Bearer ')[1];
  if (!token) {
    return res.status(401).json({ error: 'No autorizado' });
  }
  try {
    const decodedToken = await admin.auth().verifyIdToken(token);
    const userId = decodedToken.uid;
    const userDoc = await db.collection('users').doc(userId).get();
    if (!userDoc.exists) {
      return res.status(404).json({ error: 'Usuario no encontrado' });
    }
    res.json({
      success: true,
      data: { id: userId, ...userDoc.data() }
    });
  } catch (error) {
    res.status(401).json({ error: 'Token inválido' });
  }
});
Enter fullscreen mode Exit fullscreen mode

Desplegar:

firebase deploy --only functions:getUserProfile
Enter fullscreen mode Exit fullscreen mode

Consumir desde el cliente:

async function getUserProfile(token) {
  const response = await fetch(
    'https://us-central1-tu-app.cloudfunctions.net/getUserProfile',
    {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );
  const data = await response.json();
  return data;
}
Enter fullscreen mode Exit fullscreen mode

Disparadores de Base de Datos

const { onDocumentWritten } = require('firebase-functions/v2/firestore');

// Cuando el usuario cambia
exports.onUserUpdate = onDocumentWritten(
  'users/{userId}',
  async (event) => {
    const userId = event.params.userId;
    const before = event.data?.before?.data();
    const after = event.data?.after?.data();
    if (before?.email !== after?.email) {
      console.log(`El correo electrónico del usuario ${userId} cambió: ${before?.email}${after?.email}`);
      // Agrega lógica de notificación
    }
  }
);

// Nueva publicación creada
exports.onNewPost = onDocumentWritten(
  'posts/{postId}',
  async (event) => {
    const post = event.data?.after?.data();
    if (!post) return; // Documento eliminado
    if (!event.data?.before?.exists) {
      console.log('Nueva publicación creada:', post.title);
      const followersSnap = await admin.firestore()
        .collection('users')
        .where('following', 'array-contains', post.authorId)
        .get();
      const notifications = followersSnap.docs.map(doc => ({
        userId: doc.id,
        postId: event.params.postId,
        type: 'new_post',
        createdAt: admin.firestore.FieldValue.serverTimestamp()
      }));
      const batch = admin.firestore().batch();
      notifications.forEach(notif => {
        const ref = admin.firestore().collection('notifications').doc();
        batch.set(ref, notif);
      });
      await batch.commit();
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Funciones Programadas (Trabajos Cron)

const { onSchedule } = require('firebase-functions/v2/scheduler');

// Cada día a medianoche UTC
exports.dailyCleanup = onSchedule('every 24 hours', async (event) => {
  console.log('Ejecutando limpieza diaria');
  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
  const oldNotifs = await admin.firestore()
    .collection('notifications')
    .where('createdAt', '<', thirtyDaysAgo)
    .get();
  const batch = admin.firestore().batch();
  oldNotifs.forEach(doc => batch.delete(doc.ref));
  await batch.commit();
  console.log(`Eliminadas ${oldNotifs.size} notificaciones antiguas`);
});
Enter fullscreen mode Exit fullscreen mode

Configuración de Entorno

# Setear variables
firebase functions:config:set \
  stripe.secret="sk_test_xxx" \
  email.api_key="key_xxx"
Enter fullscreen mode Exit fullscreen mode
const config = require('firebase-functions/config');
const stripe = require('stripe')(config.stripe.secret);
Enter fullscreen mode Exit fullscreen mode

Cloud Storage: Carga y Gestión de Archivos

Almacena cargas, imágenes y archivos con CDN.

Configurar Reglas de Almacenamiento

// Consola de Firebase > Storage > Reglas
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {

    match /users/{userId}/{allPaths=**} {
      allow read: if true; // Lectura pública
      allow write: if request.auth.uid == userId;
      allow delete: if request.auth.uid == userId;
    }

    match /public/{allPaths=**} {
      allow read: if true;
      allow write: if false;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Cargar Archivos (Cliente)

import {
  getStorage,
  ref,
  uploadBytesResumable,
  getDownloadURL
} from 'firebase/storage';

const storage = getStorage(app);

async function uploadProfileImage(userId, file) {
  const storageRef = ref(storage, `users/${userId}/profile/${file.name}`);
  const uploadTask = uploadBytesResumable(storageRef, file);
  return new Promise((resolve, reject) => {
    uploadTask.on(
      'state_changed',
      (snapshot) => {
        const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        console.log(`Carga: ${progress.toFixed(0)}%`);
      },
      (error) => {
        switch (error.code) {
          case 'storage/unauthorized':
            reject(new Error('No tienes permiso'));
            break;
          case 'storage/canceled':
            reject(new Error('Carga cancelada'));
            break;
          default:
            reject(new Error('Fallo la carga'));
        }
      },
      async () => {
        const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
        console.log('Archivo disponible en:', downloadURL);
        resolve(downloadURL);
      }
    );
  });
}

// Uso
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

if (file) {
  const imageUrl = await uploadProfileImage(auth.currentUser.uid, file);
  await updateDoc(doc(db, 'users', auth.currentUser.uid), {
    profileImage: imageUrl
  });
}
Enter fullscreen mode Exit fullscreen mode

Descargar Archivos

import { getDownloadURL } from 'firebase/storage';

async function getProfileImage(userId) {
  const imageRef = ref(storage, `users/${userId}/profile/avatar.png`);
  try {
    const url = await getDownloadURL(imageRef);
    return url;
  } catch (error) {
    if (error.code === 'storage/object-not-found') {
      return null;
    }
    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Eliminar Archivos

import { deleteObject } from 'firebase/storage';

async function deleteProfileImage(userId) {
  const imageRef = ref(storage, `users/${userId}/profile/avatar.png`);
  await deleteObject(imageRef);
  console.log('Imagen de perfil eliminada');
}
Enter fullscreen mode Exit fullscreen mode

Probando APIs de Firebase con Apidog

Firebase expone APIs REST para todos los servicios. Probarlos ayuda a depurar y entender las solicitudes.

Importar API REST de Firebase

  1. Abre Apidog
  2. Crea un proyecto: "Firebase API"
  3. Importa especificación OpenAPI o añade endpoints manualmente

Ejemplo Firestore REST:

POST https://firestore.googleapis.com/v1/projects/{projectId}/databases/(default)/documents
Authorization: Bearer {oauth2_token}
Content-Type: application/json

{
  "fields": {
    "name": { "stringValue": "John" },
    "email": { "stringValue": "john@example.com" },
    "age": { "integerValue": 30 }
  }
}
Enter fullscreen mode Exit fullscreen mode

Autenticación:

POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secret123",
  "returnSecureToken": true
}
Enter fullscreen mode Exit fullscreen mode

Probar Flujo de Autenticación

  1. Crea solicitud "Iniciar Sesión"
  2. Método POST, cuerpo con email/contraseña
  3. Guarda el token de respuesta como variable de entorno
  4. Usa {{token}} en peticiones siguientes

Depurar Reglas de Seguridad

Utiliza Firebase Emulator Suite:

firebase emulators:start
# Prueba contra http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Mejores Prácticas de Producción

1. Manejo de Errores

async function firestoreWithRetry(operation, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation();
    } catch (error) {
      if (
        error.code === 'unavailable' ||
        error.code === 'deadline-exceeded'
      ) {
        const delay = Math.pow(2, i) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Optimizar Consultas

Agrega índices compuestos para consultas multifield:

const q = query(
  collection(db, 'posts'),
  where('category', '==', 'tech'),
  where('views', '>', 1000),
  orderBy('views', 'desc')
);
Enter fullscreen mode Exit fullscreen mode

Firestore te pedirá crear el índice cuando sea necesario.

3. Operaciones por Lotes

import { writeBatch } from 'firebase/firestore';

async function bulkUpdate(userIds, updates) {
  const batch = writeBatch(db);
  userIds.forEach(id => {
    const ref = doc(db, 'users', id);
    batch.update(ref, updates);
  });
  await batch.commit();
  console.log(`Actualizados ${userIds.length} usuarios`);
}
// Máximo 500 operaciones por lote
Enter fullscreen mode Exit fullscreen mode

4. Monitorear Costos

Servicio Nivel Gratuito De Pago
Firestore 50K lecturas/día $0.036/100K lecturas
Storage 5GB $0.023/GB
Functions 2M invocaciones $0.40/1M
Auth 10K/mes $0.0055/100K

Configura alertas de presupuesto en Google Cloud Console.

5. Proteger Cuentas de Servicio

// MAL: No usar en frontend
admin.initializeApp({
  credential: admin.credential.cert(require('./serviceAccountKey.json'))
});

// CORRECTO: Solo en servidores
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});
Enter fullscreen mode Exit fullscreen mode

6. Manejar Sin Conexión

import { enableMultiTabIndexedDbPersistence } from 'firebase/firestore';

enableMultiTabIndexedDbPersistence(db)
  .catch((err) => {
    if (err.code === 'failed-precondition') {
      // Múltiples pestañas abiertas
    } else if (err.code === 'unimplemented') {
      // El navegador no es compatible
    }
  });

import { onSnapshot } from 'firebase/firestore';

onSnapshot(doc(db, 'status', 'online'), (doc) => {
  if (!doc.exists()) {
    console.log('Estás sin conexión');
    // Mostrar UI sin conexión
  }
});
Enter fullscreen mode Exit fullscreen mode

Problemas Comunes de la API de Firebase y Soluciones

Problema 1: Errores PERMISSION_DENIED

Síntoma: Error: 7 PERMISSION_DENIED

Solución:

  1. Verifica reglas en la consola
  2. Comprueba que request.auth.uid es correcto
  3. Usa el Rules Playground

Problema 2: Expiración del Token

const user = auth.currentUser;
if (user) {
  await user.getIdToken(true); // Forzar actualización
}
Enter fullscreen mode Exit fullscreen mode

Problema 3: Latencia de Inicio en Frío

exports.keepWarm = onSchedule('every 60 seconds', async () => {
  await fetch('https://tu-funcion.cloudfunctions.net/health');
});
Enter fullscreen mode Exit fullscreen mode

Problema 4: Consulta Vacía

Causa: Falta índice o campos mal ordenados.

Solución: Revisa Firestore > Índices y crea los requeridos.

Casos de Uso en el Mundo Real

Fintech: Transacciones en Tiempo Real

Una startup de pagos usa Firestore y Cloud Functions para notificaciones instantáneas. Resultado: 40% menos tickets de soporte sobre transacciones "pendientes".

E-commerce: Sincronización de Inventario

Minoristas sincronizan stock entre web, iOS y Android con escuchas Firestore. La persistencia offline permite operaciones aún sin conectividad.

SaaS: Autenticación Multitenant

Una plataforma B2B usa Firebase Auth y reclamaciones personalizadas para roles, validando via Cloud Functions y Firestore. Más de 500 organizaciones, datos aislados.

Conclusión

La integración de la API de Firebase abarca:

  • Autenticación: Login con email, Google, Apple, tokens JWT
  • Firestore: NoSQL, escuchas en tiempo real, reglas de seguridad
  • Cloud Functions: Backend serverless por eventos o HTTP
  • Storage: Carga de archivos con CDN

Has visto flujos de autenticación, operaciones de base de datos, despliegue de funciones y gestión de archivos. Aplica patrones de producción: manejo de errores, procesamiento por lotes, soporte sin conexión y seguridad.

Preguntas Frecuentes

¿Es Firebase de uso gratuito?

Sí, Firebase tiene un nivel gratuito (Spark) con 5 GB de storage, 50K lecturas Firestore al día, 2M invocaciones Cloud Function y 10K usuarios Auth al mes. El plan Blaze es pago por uso.

¿Puedo usar Firebase con bases de datos existentes?

Sí. Usa Firebase Extensions para sincronizar con PostgreSQL, MySQL o MongoDB. O llama APIs externas desde Cloud Functions.

¿Cómo migro de Firebase a otra plataforma?

Exporta datos vía CLI o Firestore, usa Dataflow para grandes volúmenes. La dificultad depende de la estructura de tus datos.

¿Firebase soporta GraphQL?

No nativamente. Usa soluciones como firestore-graphql o crea una capa GraphQL con Cloud Functions y Apollo Server.

¿Puedo usar Firebase on-premise?

No. Firebase es solo para Google Cloud. Alternativas autoalojadas: Appwrite, Supabase, Nhost.

¿Cómo manejo cargas de archivos de más de 100 MB?

Usa cargas reanudables y fragmentadas (el SDK lo soporta). Para archivos muy grandes, usa Google Cloud Storage directamente con URLs firmadas.

¿Qué sucede si excedo los límites de consulta de Firestore?

Obtendrás error FAILED_PRECONDITION. Crea los índices requeridos o reestructura tus consultas. Firestore te da enlaces directos en el mensaje de error.

¿Firebase cumple con el GDPR?

Sí, es compatible con GDPR. Habilita residencia de datos, exportación/eliminación de usuarios y firma la Enmienda de Procesamiento de Datos de Google.

Top comments (0)