Firebase
Firebase is Google's Backend-as-a-Service (BaaS) platform that provides databases, authentication, hosting, cloud functions, and more. Created in 2011 and acquired by Google in 2014, Firebase is especially popular for mobile apps and real-time web applications. For JavaScript/TypeScript developers, Firebase offers instant backend functionality without managing servers.
What is Firebase?
Firebase is a comprehensive app platform with multiple services:
import { initializeApp } from 'firebase/app';
import { getFirestore, collection, addDoc } from 'firebase/firestore';
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
// Initialize Firebase
const app = initializeApp({
apiKey: process.env.FIREBASE_API_KEY,
authDomain: 'myapp.firebaseapp.com',
projectId: 'myapp',
});
// Use services
const db = getFirestore(app);
const auth = getAuth(app);
// Add document to Firestore
await addDoc(collection(db, 'users'), {
name: 'Alice',
email: '[email protected]',
createdAt: new Date(),
});
// Sign in user
await signInWithEmailAndPassword(auth, email, password);
Core Services:
- Firestore: NoSQL document database
- Realtime Database: Real-time JSON database
- Authentication: User authentication
- Cloud Functions: Serverless backend code
- Hosting: Static file hosting
- Storage: File/image storage
- Analytics: App analytics
Firebase Firestore
Firestore is Firebase's modern NoSQL database:
Document Model
// Firestore structure
myapp/
users/
user1/
name: "Alice"
email: "[email protected]"
posts/
post1/
title: "First Post"
content: "Hello World"
Basic Operations
import {
getFirestore,
collection,
doc,
addDoc,
getDoc,
getDocs,
updateDoc,
deleteDoc,
query,
where,
} from 'firebase/firestore';
const db = getFirestore();
// Create (auto-generated ID)
const docRef = await addDoc(collection(db, 'users'), {
name: 'Alice',
email: '[email protected]',
age: 25,
createdAt: new Date(),
});
console.log('Document ID:', docRef.id);
// Create (custom ID)
await setDoc(doc(db, 'users', 'alice123'), {
name: 'Alice',
email: '[email protected]',
});
// Read single document
const docSnap = await getDoc(doc(db, 'users', docRef.id));
if (docSnap.exists()) {
console.log(docSnap.data());
}
// Read collection
const querySnapshot = await getDocs(collection(db, 'users'));
querySnapshot.forEach((doc) => {
console.log(doc.id, doc.data());
});
// Query
const q = query(collection(db, 'users'), where('age', '>=', 18));
const querySnapshot = await getDocs(q);
// Update
await updateDoc(doc(db, 'users', docRef.id), {
age: 26,
});
// Delete
await deleteDoc(doc(db, 'users', docRef.id));
Real-Time Updates
import { onSnapshot } from 'firebase/firestore';
// Listen to document changes
const unsubscribe = onSnapshot(doc(db, 'users', userId), (doc) => {
console.log('Current data:', doc.data());
});
// Listen to collection changes
const unsubscribe = onSnapshot(collection(db, 'users'), (snapshot) => {
snapshot.forEach((doc) => {
console.log('User:', doc.data());
});
});
// Cleanup
unsubscribe();
Queries
import { query, where, orderBy, limit } from 'firebase/firestore';
// Simple query
const q = query(
collection(db, 'users'),
where('age', '>=', 18)
);
// Multiple conditions
const q = query(
collection(db, 'users'),
where('age', '>=', 18),
where('status', '==', 'active')
);
// Ordering and limiting
const q = query(
collection(db, 'posts'),
orderBy('createdAt', 'desc'),
limit(10)
);
// Range query
const q = query(
collection(db, 'users'),
where('age', '>=', 18),
where('age', '<=', 65)
);
// Array contains
const q = query(
collection(db, 'posts'),
where('tags', 'array-contains', 'javascript')
);
// In query
const q = query(
collection(db, 'users'),
where('status', 'in', ['active', 'pending'])
);
Firebase Authentication
Built-in authentication with multiple providers:
Email/Password
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
} from 'firebase/auth';
const auth = getAuth();
// Sign up
const userCredential = await createUserWithEmailAndPassword(
auth,
'[email protected]',
'password123'
);
console.log('User:', userCredential.user);
// Sign in
const userCredential = await signInWithEmailAndPassword(
auth,
'[email protected]',
'password123'
);
// Sign out
await signOut(auth);
// Listen to auth state
onAuthStateChanged(auth, (user) => {
if (user) {
console.log('Signed in:', user.uid);
} else {
console.log('Signed out');
}
});
Social Providers
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
// Google Sign-In
const provider = new GoogleAuthProvider();
const result = await signInWithPopup(auth, provider);
console.log('User:', result.user);
Supported Providers:
- Email/Password
- GitHub
- Phone (SMS)
- Anonymous
- Custom (your own auth system)
Security Rules
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Users can only read/write their own data
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Anyone can read posts, but only authenticated users can create
match /posts/{postId} {
allow read: if true;
allow create: if request.auth != null;
allow update, delete: if request.auth.uid == resource.data.authorId;
}
}
}
Using Firebase with TypeScript
Type-Safe Firestore
interface User {
name: string;
email: string;
age: number;
createdAt: Date;
}
// Type-safe collection reference
import { CollectionReference, DocumentData } from 'firebase/firestore';
const usersCollection = collection(db, 'users') as CollectionReference<User>;
// Add with types
await addDoc(usersCollection, {
name: 'Alice',
email: '[email protected]',
age: 25,
createdAt: new Date(),
});
// Query with types
const q = query(usersCollection, where('age', '>=', 18));
const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
const user = doc.data(); // TypeScript knows this is User
console.log(user.name); // ✓ Type-safe
});
Firebase Admin SDK (Server-Side)
import admin from 'firebase-admin';
// Initialize Admin SDK
admin.initializeApp({
credential: admin.credential.cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY,
}),
});
const db = admin.firestore();
// Server-side operations (bypass security rules)
const users = await db.collection('users').get();
users.forEach((doc) => {
console.log(doc.id, doc.data());
});
// Verify ID token
const decodedToken = await admin.auth().verifyIdToken(idToken);
console.log('User ID:', decodedToken.uid);
Firebase Cloud Functions
Serverless backend code:
import { onRequest } from 'firebase-functions/v2/https';
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
// HTTP function
export const helloWorld = onRequest((req, res) => {
res.send('Hello from Firebase!');
});
// Firestore trigger
export const onUserCreated = onDocumentCreated('users/{userId}', (event) => {
const userData = event.data?.data();
console.log('New user:', userData);
// Send welcome email, etc.
});
// Scheduled function
export const dailyCleanup = onSchedule('every day 00:00', async () => {
// Clean up old data
});
Firebase + Framework Integration
With React
import { useEffect, useState } from 'react';
import { collection, onSnapshot } from 'firebase/firestore';
import { db } from './firebase';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
const unsubscribe = onSnapshot(collection(db, 'users'), (snapshot) => {
const userList = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setUsers(userList);
});
return () => unsubscribe();
}, []);
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
With Next.js
// app/api/users/route.ts
import admin from 'firebase-admin';
import { NextResponse } from 'next/server';
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 async function GET() {
const db = admin.firestore();
const snapshot = await db.collection('users').get();
const users = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
return NextResponse.json(users);
}
Firebase Hosting
Deploy static sites and web apps:
# Install Firebase CLI
npm install -g firebase-tools
# Login
firebase login
# Initialize project
firebase init hosting
# Deploy
firebase deploy --only hosting
// firebase.json
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
Firebase Storage
Store and serve user-generated files:
import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage';
const storage = getStorage();
// Upload file
const storageRef = ref(storage, `images/${file.name}`);
await uploadBytes(storageRef, file);
// Get download URL
const url = await getDownloadURL(storageRef);
console.log('File URL:', url);
// Upload from input
function handleFileUpload(event) {
const file = event.target.files[0];
const storageRef = ref(storage, `uploads/${file.name}`);
uploadBytes(storageRef, file).then((snapshot) => {
console.log('Uploaded!', snapshot);
});
}
Firebase Realtime Database vs. Firestore
Feature | Firestore | Realtime Database |
---|---|---|
Data Model | Collections & Documents | JSON Tree |
Queries | Rich queries | Limited queries |
Scaling | Automatic | Manual sharding |
Offline | Advanced | Basic |
Pricing | Per operation | Per GB stored |
Best For | New projects | Simple apps |
Recommendation: Use Firestore for new projects.
Pricing
Free Tier (Spark Plan):
- 1 GB storage
- 50K reads/day
- 20K writes/day
- 20K deletes/day
- Authentication (unlimited)
Pay-as-you-go (Blaze Plan):
- $0.18 per GB stored
- $0.06 per 100K reads
- $0.18 per 100K writes
- $0.02 per 100K deletes
Example Cost (10M reads/month):
- Firestore: ~$6/month
- Hosting: Free (10 GB/month)
- Functions: $0.40 per million invocations
Firebase vs. Supabase vs. MongoDB
Feature | Firebase | Supabase | MongoDB |
---|---|---|---|
Type | NoSQL | PostgreSQL (SQL) | NoSQL |
Real-time | Yes | Yes | Change Streams |
Auth | Built-in | Built-in | Third-party |
Storage | Built-in | Built-in | GridFS |
Functions | Built-in | Edge Functions | Atlas Functions |
Pricing | Pay per use | $25/month | Atlas ($9+/month) |
Open Source | No | Yes | Yes |
Best Practices
1. Structure Data for Queries
// ✓ Good - can query by status
{
status: 'active',
name: 'Alice',
}
// ❌ Bad - can't query nested object
{
metadata: {
status: 'active',
},
}
2. Use Subcollections
// ✓ Good - subcollections
users/{userId}/posts/{postId}
// Access
const postsRef = collection(db, 'users', userId, 'posts');
3. Batch Writes
import { writeBatch } from 'firebase/firestore';
const batch = writeBatch(db);
batch.set(doc(db, 'users', 'alice'), { name: 'Alice' });
batch.set(doc(db, 'users', 'bob'), { name: 'Bob' });
batch.update(doc(db, 'stats', 'userCount'), { count: 2 });
await batch.commit();
4. Use Security Rules
// Validate data structure
match /posts/{postId} {
allow create: if request.auth != null
&& request.resource.data.title is string
&& request.resource.data.title.size() <= 100;
}
Key Takeaways
- Backend-as-a-Service with database, auth, hosting, functions
- Firestore: NoSQL document database with real-time updates
- Built-in authentication with multiple providers
- Cloud Functions for serverless backend code
- Great for rapid development, mobile apps, real-time features
- Free tier is generous for side projects
- Not open source (vendor lock-in risk)
Related Topics
- Supabase - Open-source Firebase alternative
- MongoDB - Alternative NoSQL database
- Databases Overview - Compare all databases
- React - Popular framework for Firebase apps
- Next.js - Full-stack framework with Firebase
Firebase is excellent for getting started quickly with real-time features and built-in authentication. Its generous free tier and comprehensive platform make it ideal for side projects and MVPs. However, consider Supabase if you prefer open-source or need SQL features.