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.
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 │
└──────────────┴──────────────┴──────────────┴────────────┘
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
- Open the Firebase Console.
- Click Add project.
- Enter a project name without spaces.
- Enable Google Analytics if needed.
- Click Create project.
Wait for provisioning to finish, then open the project dashboard.
Step 2: Register Your App
Web
In Firebase Console → Project Settings → General:
- Click Add app.
- Choose the Web icon.
- 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);
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'
}
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}`);
}
}
}
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");
}
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");
}
}
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";
});
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.
}
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
Initialize Firestore
import { getFirestore } from "firebase/firestore";
const db = getFirestore(app);
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"
});
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);
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")
});
Delete Documents
import { deleteDoc, doc } from "firebase/firestore";
async function deleteUser(userId) {
await deleteDoc(doc(db, "users", userId));
console.log("User deleted");
}
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();
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);
}
}
}
Firestore Query Constraints
Plan data models around Firestore limitations:
- No arbitrary
ORqueries. Useinqueries or combine multiple requests. - No wildcard or full-text search. Use Algolia or Meilisearch for search.
- Multi-field queries may require composite indexes.
-
inqueries 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()
}));
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
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"
});
}
});
Deploy a single function:
firebase deploy --only functions:getUserProfile
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();
}
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();
}
}
);
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`);
});
Configure Environment Variables
firebase functions:config:set \
stripe.secret="sk_test_xxx" \
email.api_key="key_xxx"
Access configuration from the function:
const config = require("firebase-functions/config");
const stripe = require("stripe")(config.stripe.secret);
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;
}
}
}
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
});
}
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;
}
}
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");
}
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:
- Create a project named Firebase API.
- Import an OpenAPI specification from Firebase documentation, or add requests manually.
- 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
{
"fields": {
"name": { "stringValue": "John" },
"email": { "stringValue": "john@example.com" },
"age": { "integerValue": 30 }
}
}
Example Authentication request:
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123",
"returnSecureToken": true
}
Test an Authentication Flow
- Create a
Sign Inrequest. - Set the method to
POST. - Add the email and password JSON body.
- Save the returned token as an environment variable.
- Use
{{token}}in theAuthorizationheader of protected requests.
Test Rules Locally with the Emulator Suite
firebase emulators:start
The local Firestore emulator runs at:
http://localhost:8080
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;
}
}
}
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")
);
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`);
}
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")
)
});
Use environment-managed credentials only in server environments:
const serviceAccount = JSON.parse(
process.env.FIREBASE_SERVICE_ACCOUNT
);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
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.
}
});
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.
}
});
Common Firebase API Issues and Fixes
Permission Denied Errors
Symptom
Error: 7 PERMISSION_DENIED
Cause: Firebase Security Rules block the operation.
Fix:
- Review rules in Firebase Console.
- Confirm
request.auth.uidmatches the expected user. - Test rules with the Rules Playground or Emulator Suite.
Expired Tokens
Symptom
Error: ID token expired
Force a token refresh before retrying the request:
const user = auth.currentUser;
if (user) {
await user.getIdToken(true);
}
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");
});
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)