DEV Community

Cover image for How to Use Firebase API: Complete Integration Guide (2026)
Preecha
Preecha

Posted on

How to Use Firebase API: Complete Integration Guide (2026)

You’re building an app. Users need to sign in, data needs to sync in real time, and files need storage. You can spend weeks managing servers, databases, and infrastructure—or use Firebase.

Try Apidog today

Firebase powers over 1.5 million apps, including The New York Times, Duolingo, and Alibaba. Its SDKs remove much of the backend setup so you can focus on features instead of server maintenance. The tradeoff is that Firebase has important implementation details: authentication flows, database rules, query constraints, and Cloud Function triggers can all cause production issues if you treat them as magic.

This guide walks through Firebase Authentication, Firestore, Cloud Functions, and Cloud Storage with working JavaScript examples. It also covers security rules, testing, common failures, and production patterns.

💡 Testing Firebase APIs is easier with an API client. Apidog can help you organize endpoints, test authentication flows, and share request collections with your team.

What Is the Firebase API?

Firebase is not a single API. It is a set of backend services available through Firebase SDKs, REST endpoints, and deployment tooling.

Core Firebase Services

Service Purpose API type
Authentication User sign-in and identity SDK + REST
Firestore Database NoSQL document database SDK + REST
Realtime Database JSON real-time synchronization SDK + REST
Cloud Storage File storage and CDN delivery SDK + REST
Cloud Functions Serverless compute Deployment CLI
Hosting Static web hosting Deployment CLI
Cloud Messaging Push notifications HTTP v1 API

When Firebase Makes Sense

Use Firebase when you need:

  • Real-time synchronization for chat, collaboration, or live updates.
  • A serverless architecture without managing infrastructure.
  • Mobile and web SDKs that handle platform differences.
  • Offline support through SDK caching.
  • Built-in authentication for Google, Apple, email/password, or phone sign-in.

Consider another approach when you need:

  • Complex relational queries—PostgreSQL is often a better fit.
  • Strict data-residency requirements.
  • Full SQL capabilities.
  • Lower infrastructure costs at very large scale, where self-hosting may be cheaper.

Firebase Architecture

Firebase client SDKs handle authentication tokens, offline caching, and real-time listeners. Under the hood, client operations become authenticated API requests over HTTPS and WebSockets.

┌─────────────────────────────────────────────────────────┐
│                    Your Application                      │
├─────────────────────────────────────────────────────────┤
│  Firebase SDK (Client)                                   │
│  - Auto-handles auth tokens                              │
│  - Manages offline cache                                 │
│  - Real-time listeners                                   │
└─────────────────────────────────────────────────────────┘
                          │
                          │ HTTPS + WebSocket
                          ▼
┌─────────────────────────────────────────────────────────┐
│                   Firebase Backend                       │
├──────────────┬──────────────┬──────────────┬────────────┤
│   Auth       │  Firestore   │   Storage    │ Functions  │
│   Service    │  Database    │   Service    │  Runtime   │
└──────────────┴──────────────┴──────────────┴────────────┘
Enter fullscreen mode Exit fullscreen mode

Firebase Authentication: Complete Setup

Authentication is usually the first Firebase integration. Set it up before writing database rules or protected backend endpoints.

Step 1: Create a Firebase Project

  1. Open the Firebase Console.
  2. Click Add project.
  3. Enter a project name without spaces.

Image

Image

  1. Enable Google Analytics if needed.

Image

  1. Click Create project.

Image

Wait for provisioning to finish, then open the project dashboard.

Step 2: Register Your App

Web

In Firebase Console → Project Settings → General:

  1. Click Add app.
  2. Choose the Web icon.
  3. Copy the generated configuration into your application.
const firebaseConfig = {
  apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  authDomain: "your-app.firebaseapp.com",
  projectId: "your-app",
  storageBucket: "your-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

Download GoogleService-Info.plist and add it to your Xcode project. Confirm that Target Membership includes your app target.

Android

Download google-services.json and place it in your app module directory.

Add the Google Services plugin:

// Project-level build.gradle
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.0'
    }
}

// App-level build.gradle
plugins {
    id 'com.google.gms.google-services'
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Enable Sign-In Methods

In Firebase Console → Authentication → Sign-in method, enable the providers your app needs:

  • Email/Password for standard account registration.
  • Google for Google OAuth. Configure SHA-1 fingerprints on Android or bundle IDs on iOS.
  • Apple when required for iOS apps that offer other social login providers.
  • Phone for SMS-based sign-in. This requires billing.

Step 4: Implement Email and Password Sign-Up

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("User created:", userCredential.user.uid);

    return userCredential.user;
  } catch (error) {
    switch (error.code) {
      case "auth/email-already-in-use":
        throw new Error("This email is already registered");
      case "auth/weak-password":
        throw new Error("Password must be at least 6 characters");
      case "auth/invalid-email":
        throw new Error("Invalid email address");
      default:
        throw new Error(`Sign up failed: ${error.message}`);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Implement Sign-In and Sign-Out

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

async function signIn(email, password) {
  try {
    const userCredential = await signInWithEmailAndPassword(
      auth,
      email,
      password
    );

    const user = userCredential.user;

    // Use this token when calling your own protected APIs.
    const idToken = await user.getIdToken();

    console.log("Auth token:", idToken);

    return user;
  } catch (error) {
    switch (error.code) {
      case "auth/user-not-found":
        throw new Error("No account with this email");
      case "auth/wrong-password":
        throw new Error("Incorrect password");
      case "auth/too-many-requests":
        throw new Error("Too many attempts. Try again later");
      default:
        throw new Error("Sign in failed");
    }
  }
}

async function logOut() {
  await signOut(auth);
  console.log("User signed out");
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Add Google Sign-In for 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("Sign-in cancelled");
    }

    throw new Error("Google sign-in failed");
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Protect Routes with Authentication State

Subscribe once to authentication state changes and redirect users based on the current session.

import { onAuthStateChanged } from "firebase/auth";

onAuthStateChanged(auth, (user) => {
  if (user) {
    console.log("User:", user.email);
    window.location.href = "/dashboard";
    return;
  }

  console.log("No user");
  window.location.href = "/login";
});
Enter fullscreen mode Exit fullscreen mode

Common Authentication Mistakes

Do not cache ID tokens indefinitely

Firebase SDKs refresh tokens automatically. If your server caches a token, it expires after one hour. Verify the token on every protected request, or implement explicit refresh logic.

Never expose service account credentials

Do not add Firebase Admin SDK keys to browser, iOS, or Android code. Service accounts bypass Firebase Security Rules and must only run in trusted server environments.

Require email verification when appropriate

import { sendEmailVerification } from "firebase/auth";

async function sendVerificationEmail(user) {
  await sendEmailVerification(user);
  console.log("Verification email sent");
}

if (!auth.currentUser.emailVerified) {
  console.log("Email not verified");
  // Restrict access to verification-required features.
}
Enter fullscreen mode Exit fullscreen mode

Firestore Database: Documents, Queries, and Rules

Firestore is Firebase’s NoSQL document database. Data is organized into collections and documents, with optional subcollections.

Example Data Structure

your-project
└── users
    ├── userId123
    │   ├── name: "John"
    │   ├── email: "john@example.com"
    │   └── posts
    │       ├── postId1
    │       └── postId2
    └── userId456
Enter fullscreen mode Exit fullscreen mode

Initialize Firestore

import { getFirestore } from "firebase/firestore";

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

Create Documents

Use addDoc() when Firestore should generate the document ID. Use setDoc() when you already have a stable ID, such as the authenticated user UID.

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

// Auto-generated ID
async function createUser(userData) {
  const docRef = await addDoc(collection(db, "users"), userData);

  console.log("Document written with ID:", docRef.id);

  return docRef.id;
}

// Custom ID
async function createUserWithId(userId, userData) {
  await setDoc(doc(db, "users", userId), userData);

  console.log("Document written with custom ID:", userId);
}

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

Read Documents and Query Collections

import {
  collection,
  doc,
  getDoc,
  getDocs,
  limit,
  orderBy,
  query,
  where
} from "firebase/firestore";

async function getUser(userId) {
  const docRef = doc(db, "users", userId);
  const docSnap = await getDoc(docRef);

  if (!docSnap.exists()) {
    throw new Error("User not found");
  }

  return docSnap.data();
}

async function getUsersByRole(role) {
  const q = query(
    collection(db, "users"),
    where("role", "==", role),
    orderBy("createdAt", "desc"),
    limit(10)
  );

  const querySnapshot = await getDocs(q);

  return querySnapshot.docs.map((document) => ({
    id: document.id,
    ...document.data()
  }));
}

const adminUsers = await getUsersByRole("admin");
console.log("Admin users:", adminUsers);
Enter fullscreen mode Exit fullscreen mode

Update Documents with Atomic Operations

Use Firestore atomic operators for counters and arrays rather than reading, modifying, and writing values manually.

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

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

await updateUser("userId123", {
  loginCount: increment(1),
  tags: arrayUnion("premium", "beta-tester"),
  lastLogin: new Date()
});

await updateUser("userId123", {
  tags: arrayRemove("beta-tester")
});
Enter fullscreen mode Exit fullscreen mode

Delete Documents

import { deleteDoc, doc } from "firebase/firestore";

async function deleteUser(userId) {
  await deleteDoc(doc(db, "users", userId));
  console.log("User deleted");
}
Enter fullscreen mode Exit fullscreen mode

Subscribe to Real-Time Changes

Always keep the unsubscribe function and call it when a component unmounts or a page no longer needs the listener.

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

const unsubscribe = onSnapshot(
  doc(db, "users", userId),
  (document) => {
    console.log("User updated:", document.data());
  },
  (error) => {
    console.error("Listen error:", error);
  }
);

const q = query(
  collection(db, "posts"),
  where("published", "==", true)
);

const unsubscribeQuery = onSnapshot(q, (snapshot) => {
  const posts = snapshot.docs.map((document) => ({
    id: document.id,
    ...document.data()
  }));

  console.log("Published posts:", posts);
});

// Call these when listeners are no longer needed.
unsubscribe();
unsubscribeQuery();
Enter fullscreen mode Exit fullscreen mode

Secure Firestore with Rules

Without rules, clients may be able to read or modify data they should not access. Configure rules in Firebase Console → Firestore Database → Rules.

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;
      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

Firestore Query Constraints

Plan data models around Firestore limitations:

  • No arbitrary OR queries. Use in queries or combine multiple requests.
  • No wildcard or full-text search. Use Algolia or Meilisearch for search.
  • Multi-field queries may require composite indexes.
  • in queries support up to 30 disjunctions.

For example, run separate queries and merge results when you need an OR condition:

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)
]);

const tasks = [
  ...activeSnap.docs,
  ...pendingSnap.docs
].map((document) => ({
  id: document.id,
  ...document.data()
}));
Enter fullscreen mode Exit fullscreen mode

Cloud Functions: Serverless Backend Logic

Cloud Functions run backend code without managing servers. You can trigger functions from HTTP requests, Firestore changes, and schedules.

Set Up Cloud Functions

# Install Firebase CLI
npm install -g firebase-tools

# Authenticate
firebase login

# Initialize Functions
firebase init functions
Enter fullscreen mode Exit fullscreen mode

During setup, select:

  • JavaScript
  • ESLint: Yes
  • Express.js: No

Create HTTP API Endpoints

Use the Admin SDK inside Cloud Functions for trusted server-side operations.

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

admin.initializeApp();

const db = admin.firestore();

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((document) => document.data());

    res.json({
      success: true,
      data
    });
  } catch (error) {
    res.status(500).json({
      error: error.message
    });
  }
});

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: "Unauthorized"
    });
  }

  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: "User not found"
      });
    }

    return res.json({
      success: true,
      data: {
        id: userId,
        ...userDoc.data()
      }
    });
  } catch (error) {
    return res.status(401).json({
      error: "Invalid token"
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

Deploy a single function:

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

Call it from the client with the Firebase ID token:

async function getUserProfile(token) {
  const response = await fetch(
    "https://us-central1-your-app.cloudfunctions.net/getUserProfile",
    {
      headers: {
        Authorization: `Bearer ${token}`
      }
    }
  );

  return response.json();
}
Enter fullscreen mode Exit fullscreen mode

Trigger Functions from Firestore Changes

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

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(
        `User ${userId} email changed: ${before?.email}${after?.email}`
      );

      await admin.auth().getUser(userId);

      // Add email notification logic here.
    }
  }
);

exports.onNewPost = onDocumentWritten(
  "posts/{postId}",
  async (event) => {
    const post = event.data?.after?.data();

    if (!post) {
      return;
    }

    if (!event.data?.before?.exists) {
      console.log("New post created:", post.title);

      const followersSnap = await admin
        .firestore()
        .collection("users")
        .where("following", "array-contains", post.authorId)
        .get();

      const notifications = followersSnap.docs.map((document) => ({
        userId: document.id,
        postId: event.params.postId,
        type: "new_post",
        createdAt: admin.firestore.FieldValue.serverTimestamp()
      }));

      const batch = admin.firestore().batch();

      notifications.forEach((notification) => {
        const ref = admin.firestore().collection("notifications").doc();
        batch.set(ref, notification);
      });

      await batch.commit();
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Run Scheduled Jobs

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

exports.dailyCleanup = onSchedule("every 24 hours", async () => {
  console.log("Running daily cleanup");

  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((document) => batch.delete(document.ref));

  await batch.commit();

  console.log(`Deleted ${oldNotifs.size} old notifications`);
});
Enter fullscreen mode Exit fullscreen mode

Configure Environment Variables

firebase functions:config:set \
  stripe.secret="sk_test_xxx" \
  email.api_key="key_xxx"
Enter fullscreen mode Exit fullscreen mode

Access configuration from the function:

const config = require("firebase-functions/config");
const stripe = require("stripe")(config.stripe.secret);
Enter fullscreen mode Exit fullscreen mode

Cloud Storage: Upload, Download, and Delete Files

Cloud Storage is useful for user uploads, images, and other files. Firebase handles storage access through SDKs and can distribute content through a CDN.

Configure Storage Rules

Set rules in Firebase Console → Storage → Rules.

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{allPaths=**} {
      allow read: if true;
      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

Upload Files from the Client

import {
  getDownloadURL,
  getStorage,
  ref,
  uploadBytesResumable
} 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(`Upload: ${progress.toFixed(0)}%`);
      },
      (error) => {
        switch (error.code) {
          case "storage/unauthorized":
            reject(new Error("You do not have permission"));
            break;
          case "storage/canceled":
            reject(new Error("Upload cancelled"));
            break;
          default:
            reject(new Error("Upload failed"));
        }
      },
      async () => {
        const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);

        console.log("File available at:", downloadURL);

        resolve(downloadURL);
      }
    );
  });
}

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

Download Files

import { getDownloadURL, ref } from "firebase/storage";

async function getProfileImage(userId) {
  const imageRef = ref(
    storage,
    `users/${userId}/profile/avatar.png`
  );

  try {
    return await getDownloadURL(imageRef);
  } catch (error) {
    if (error.code === "storage/object-not-found") {
      return null;
    }

    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Delete Files

import { deleteObject, ref } from "firebase/storage";

async function deleteProfileImage(userId) {
  const imageRef = ref(
    storage,
    `users/${userId}/profile/avatar.png`
  );

  await deleteObject(imageRef);

  console.log("Profile image deleted");
}
Enter fullscreen mode Exit fullscreen mode

Testing Firebase APIs with Apidog

Firebase exposes REST APIs for its services. Calling them directly is useful for debugging SDK behavior, validating tokens, and understanding request payloads.

Add Firebase REST Requests

In Apidog:

  1. Create a project named Firebase API.
  2. Import an OpenAPI specification from Firebase documentation, or add requests manually.
  3. Create environments for your Firebase project ID, API key, and auth token.

Example Firestore REST request:

POST https://firestore.googleapis.com/v1/projects/{projectId}/databases/(default)/documents
Authorization: Bearer {oauth2_token}
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode
{
  "fields": {
    "name": { "stringValue": "John" },
    "email": { "stringValue": "john@example.com" },
    "age": { "integerValue": 30 }
  }
}
Enter fullscreen mode Exit fullscreen mode

Example Authentication request:

POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode
{
  "email": "user@example.com",
  "password": "secret123",
  "returnSecureToken": true
}
Enter fullscreen mode Exit fullscreen mode

Test an Authentication Flow

  1. Create a Sign In request.
  2. Set the method to POST.
  3. Add the email and password JSON body.
  4. Save the returned token as an environment variable.
  5. Use {{token}} in the Authorization header of protected requests.

Test Rules Locally with the Emulator Suite

firebase emulators:start
Enter fullscreen mode Exit fullscreen mode

The local Firestore emulator runs at:

http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Use the emulator before deploying rule changes to production.

Production Best Practices

1. Retry Transient Firestore Failures

Retry only temporary failures such as unavailable services and deadlines.

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. Create Composite Indexes for Multi-Field Queries

This query needs a composite index:

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

Firestore provides a direct index-creation link when a query requires one.

3. Use Batch Writes

Use batch writes when updating multiple documents together.

import {
  doc,
  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(`Updated ${userIds.length} users`);
}
Enter fullscreen mode Exit fullscreen mode

Firestore supports a maximum of 500 operations per batch.

4. Monitor Costs

Service Free tier Paid usage
Firestore 50K reads/day $0.036/100K reads
Storage 5 GB $0.023/GB
Functions 2M invocations $0.40/1M
Auth 10K/month $0.0055/100K

Set budget alerts in Google Cloud Console so unexpected traffic does not become an unexpected bill.

5. Keep Service Accounts on Trusted Servers

Never load service account files in client-side code.

// Wrong: never do this in browser, iOS, or Android code.
admin.initializeApp({
  credential: admin.credential.cert(
    require("./serviceAccountKey.json")
  )
});
Enter fullscreen mode Exit fullscreen mode

Use environment-managed credentials only in server environments:

const serviceAccount = JSON.parse(
  process.env.FIREBASE_SERVICE_ACCOUNT
);

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount)
});
Enter fullscreen mode Exit fullscreen mode

6. Handle Offline Scenarios

Enable multi-tab IndexedDB persistence for web applications:

import {
  enableMultiTabIndexedDbPersistence
} from "firebase/firestore";

enableMultiTabIndexedDbPersistence(db).catch((error) => {
  if (error.code === "failed-precondition") {
    // Multiple tabs are open.
  } else if (error.code === "unimplemented") {
    // Browser does not support persistence.
  }
});
Enter fullscreen mode Exit fullscreen mode

You can also use document listeners to update offline UI state:

import {
  doc,
  onSnapshot
} from "firebase/firestore";

onSnapshot(doc(db, "status", "online"), (document) => {
  if (!document.exists()) {
    console.log("You are offline");
    // Show offline UI.
  }
});
Enter fullscreen mode Exit fullscreen mode

Common Firebase API Issues and Fixes

Permission Denied Errors

Symptom

Error: 7 PERMISSION_DENIED
Enter fullscreen mode Exit fullscreen mode

Cause: Firebase Security Rules block the operation.

Fix:

  • Review rules in Firebase Console.
  • Confirm request.auth.uid matches the expected user.
  • Test rules with the Rules Playground or Emulator Suite.

Expired Tokens

Symptom

Error: ID token expired
Enter fullscreen mode Exit fullscreen mode

Force a token refresh before retrying the request:

const user = auth.currentUser;

if (user) {
  await user.getIdToken(true);
}
Enter fullscreen mode Exit fullscreen mode

Cloud Function Cold Starts

Symptom: A Cloud Function takes 2–5 seconds on its first call.

You can schedule periodic health requests:

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

Empty Firestore Query Results

Symptom: A query returns an empty array when you expect data.

Possible causes:

  • A missing composite index.
  • Incorrect field names or field ordering.
  • Security Rules preventing reads.

Check Firestore Console → Indexes and confirm the query matches the stored document shape.

Real-World Use Cases

Fintech: Real-Time Transaction Updates

A payment startup used Firebase Firestore for real-time transaction notifications. When a payment processes, Cloud Functions trigger updates to connected admin dashboards within 200ms. The result was a 40% reduction in support tickets about pending transactions.

E-Commerce: Inventory Synchronization

An online retailer synchronizes inventory across web, iOS, and Android clients with Firestore listeners. Stock changes appear across connected clients automatically. Offline persistence lets warehouse workers scan items without connectivity and sync changes when they reconnect.

SaaS: Multi-Tenant Authentication

A B2B platform uses Firebase Authentication with custom claims for role-based access. Cloud Functions validate permissions against Firestore tenant configurations. A single codebase serves more than 500 organizations while isolating tenant data.

Firebase handles real-time synchronization and authentication cleanly, but Firebase's biggest open-source rival takes a different approach to the same problems. A Supabase CLI workflow focuses on local development, migrations, and type generation on the Postgres side.

If your backend is a commerce platform, the integration model is different again. Magento 2 REST and GraphQL APIs use their own token flows and resource hierarchy, which need a separate setup pass.

Conclusion

A practical Firebase integration usually relies on four core services:

  • Authentication: Email, Google, Apple, and phone sign-in with JWT tokens.
  • Firestore: A NoSQL database with real-time listeners and Security Rules.
  • Cloud Functions: Serverless backend logic triggered by HTTP, events, or schedules.
  • Cloud Storage: File uploads and downloads with access rules.

Start with Authentication and Security Rules, then add Firestore data access, Cloud Functions for trusted backend logic, and Storage for uploads. Use retries, batch writes, indexes, offline support, and local emulators before shipping to production.

FAQ

Is Firebase free to use?

Yes. Firebase has a free Spark Plan that includes 5 GB of storage, 50K Firestore reads per day, 2M Cloud Function invocations, and 10K Auth users per month. The Blaze plan uses pay-as-you-go pricing.

Can I use Firebase with existing databases?

Yes. You can use Firebase Extensions to synchronize with PostgreSQL, MySQL, or MongoDB. You can also call external APIs from Cloud Functions.

How do I migrate from Firebase to another platform?

Export data with Firestore export functions or the Firebase CLI. For large datasets, use the Dataflow export pipeline. Migration complexity depends on your data model.

Does Firebase support GraphQL?

Not natively. Use a third-party option such as firestore-graphql, or build a GraphQL layer with Cloud Functions and Apollo Server.

Can I run Firebase on-premise?

No. Firebase runs on Google Cloud. For self-hosted alternatives, consider Appwrite, Supabase, or Nhost.

How do I upload files larger than 100 MB?

Use resumable uploads with chunking. The Firebase SDK handles resumable uploads automatically. For very large files, use Google Cloud Storage with signed URLs.

What happens if I exceed Firestore query limits?

The query fails with a FAILED_PRECONDITION error. Add the required index or restructure the query. Firestore error messages include direct links for creating missing indexes.

Is Firebase GDPR compliant?

Yes. Firebase offers GDPR-compliant data processing. Configure regional data residency where available, implement user-data export and deletion flows, and sign Google’s Data Processing Amendment.

Top comments (0)