Bạn đang xây dựng một ứng dụng. Người dùng cần đăng nhập. Dữ liệu cần đồng bộ hóa theo thời gian thực. Các tệp cần được lưu trữ. Bạn có thể thiết lập máy chủ, cấu hình cơ sở dữ liệu và quản lý hạ tầng trong nhiều tuần. Hoặc bạn có thể sử dụng Firebase.
Firebase cung cấp sức mạnh cho hơn 1,5 triệu ứng dụng bao gồm The New York Times, Duolingo và Alibaba. Các nhà phát triển chọn nó vì nó loại bỏ sự phức tạp của phần phụ trợ (backend). Bạn tập trung vào các tính năng, không phải bảo trì máy chủ. Nhưng API Firebase có những điểm khác biệt. Luồng xác thực làm người mới bối rối. Quy tắc cơ sở dữ liệu làm các nhà phát triển có kinh nghiệm gặp khó khăn. Cloud Functions có vẻ kỳ diệu cho đến khi bạn hiểu các kích hoạt của chúng.
Tôi đã tích hợp Firebase vào các ứng dụng sản xuất phục vụ hàng triệu người dùng. Tôi đã mắc mọi lỗi có thể: làm lộ khóa tài khoản dịch vụ, viết các truy vấn kém hiệu quả, triển khai các hàm bị lỗi. Hướng dẫn này chắt lọc những bài học đó.
Bạn sẽ học về xác thực, các thao tác cơ sở dữ liệu, Cloud Functions và lưu trữ. Bạn sẽ thấy mã hoạt động thực tế, không chỉ lý thuyết. Bạn sẽ tránh được những cạm bẫy gây ra các vấn đề nghiêm trọng trong sản xuất.
💡Kiểm thử API Firebase trở nên dễ dàng hơn với các công cụ client API phù hợp. Apidog cho phép bạn tổ chức các điểm cuối, kiểm thử luồng xác thực và chia sẻ các bộ sưu tập với nhóm của bạn. Chúng tôi sẽ chỉ ra vị trí phù hợp tự nhiên của nó trong quy trình làm việc.
API Firebase là gì và tại sao nó lại quan trọng?
Firebase không phải là một API duy nhất. Đó là một bộ các dịch vụ backend được truy cập thông qua các SDK hợp nhất và các điểm cuối REST.
Các Dịch vụ Firebase Cốt lõi
| Dịch vụ | Mục đích | Loại API |
|---|---|---|
| Xác thực | Đăng nhập người dùng và định danh | SDK + REST |
| Cơ sở dữ liệu Firestore | Cơ sở dữ liệu tài liệu NoSQL | SDK + REST |
| Cơ sở dữ liệu Thời gian thực | Đồng bộ hóa JSON thời gian thực | SDK + REST |
| Cloud Storage | Lưu trữ tệp và CDN | SDK + REST |
| Cloud Functions | Tính toán phi máy chủ (Serverless compute) | CLI triển khai |
| Hosting | Lưu trữ web tĩnh | CLI triển khai |
| Cloud Messaging | Thông báo đẩy | API HTTP v1 |
Khi nào nên sử dụng Firebase
Firebase giải quyết tốt các vấn đề cụ thể:
Sử dụng Firebase khi:
- Cần đồng bộ hóa thời gian thực (trò chuyện, cộng tác, cập nhật trực tiếp)
- Muốn kiến trúc phi máy chủ (không cần quản lý hạ tầng)
- Xây dựng ứng dụng di động hoặc web (SDK xử lý sự khác biệt giữa các nền tảng)
- Cần hỗ trợ ngoại tuyến (SDK tự động lưu trữ dữ liệu)
- Muốn xác thực tích hợp sẵn (đăng nhập bằng Google, Apple, email, số điện thoại)
Không sử dụng Firebase khi:
- Cần các truy vấn quan hệ phức tạp (hãy dùng PostgreSQL)
- Có yêu cầu nghiêm ngặt về vị trí dữ liệu (các khu vực của Firebase bị hạn chế)
- Cần đầy đủ khả năng SQL (Firestore có những hạn chế về truy vấn)
- Chi phí ở quy mô lớn quan trọng hơn tốc độ phát triển (tự lưu trữ rẻ hơn)
Kiến trúc API Firebase
Firebase sử dụng một cách tiếp cận lai:
┌─────────────────────────────────────────────────────────┐
│ Ứng dụng của bạn │
├─────────────────────────────────────────────────────────┤
│ Firebase SDK (Client) │
│ - Tự động xử lý mã thông báo xác thực │
│ - Quản lý bộ nhớ đệm ngoại tuyến │
│ - Trình lắng nghe thời gian thực │
└─────────────────────────────────────────────────────────┘
│
│ HTTPS + WebSocket
▼
┌─────────────────────────────────────────────────────────┐
│ Backend Firebase │
├──────────────┬──────────────┬──────────────┬────────────┤
│ Dịch vụ │ Cơ sở dữ liệu │ Dịch vụ │ Hàm │
│ Xác thực │ Firestore │ Lưu trữ │ Thời gian chạy │
└──────────────┴──────────────┴──────────────┴────────────┘
Các SDK client trừu tượng hóa lớp HTTP. Về bản chất, mọi thao tác đều được dịch thành các lệnh gọi API REST với xác thực JWT.
Xác thực Firebase: Thiết lập Hoàn chỉnh
Xác thực là tích hợp Firebase đầu tiên của bạn. Nếu làm sai bước này, mọi thứ khác sẽ thất bại.
Bước 1: Tạo dự án Firebase
- Truy cập Firebase Console
- Nhấp vào "Thêm dự án" và nhập tên dự án (không có khoảng trắng)
- Bật Google Analytics (tùy chọn nhưng được khuyến nghị)
- Nhấp vào "Tạo dự án" và chờ hoàn tất
Bước 2: Đăng ký ứng dụng của bạn
Web:
// Trong Firebase Console > Cài đặt dự án > Tổng quan
// Nhấp vào "Thêm ứng dụng" > biểu tượng Web
const firebaseConfig = {
apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "your-app.firebaseapp.com",
projectId: "your-app",
storageBucket: "your-app.appspot.com",
messagingSenderId: "123456789012",
appId: "1:123456789012:web:abc123def456"
};
// Khởi tạo Firebase
import { initializeApp } from 'firebase/app';
const app = initializeApp(firebaseConfig);
iOS:
Tải về GoogleService-Info.plist và thêm vào dự án Xcode.
Android:
Tải về google-services.json và đặt vào thư mục app/.
Thêm vào build.gradle:
// build.gradle cấp dự án
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.0'
}
}
// build.gradle cấp ứng dụng
plugins {
id 'com.google.gms.google-services'
}
Bước 3: Bật các phương thức xác thực
Firebase Console > Xác thực > Phương thức đăng nhập:
- Email/Mật khẩu: Bật để đăng ký truyền thống
- Google: Thêm SHA-1 (Android) hoặc ID gói (iOS)
- Apple: Bắt buộc nếu bật đăng nhập xã hội trên iOS
- Điện thoại: Bật để xác thực bằng SMS (yêu cầu thanh toán)
Bước 4: Triển khai luồng xác thực
Đăng ký bằng Email/Mật khẩu:
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
);
// Đặt tên hiển thị
await updateProfile(userCredential.user, {
displayName: displayName
});
console.log('Người dùng được tạo:', userCredential.user.uid);
return userCredential.user;
} catch (error) {
switch (error.code) {
case 'auth/email-already-in-use':
throw new Error('Email này đã được sử dụng');
case 'auth/weak-password':
throw new Error('Mật khẩu phải có ít nhất 6 ký tự');
case 'auth/invalid-email':
throw new Error('Địa chỉ email không hợp lệ');
default:
throw new Error('Đăng ký thất bại: ' + error.message);
}
}
}
Đăng nhập bằng Email/Mật khẩu:
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('Mã thông báo xác thực:', idToken);
return user;
} catch (error) {
switch (error.code) {
case 'auth/user-not-found':
throw new Error('Không có tài khoản nào với email này');
case 'auth/wrong-password':
throw new Error('Mật khẩu không chính xác');
case 'auth/too-many-requests':
throw new Error('Quá nhiều lần thử. Vui lòng thử lại sau');
default:
throw new Error('Đăng nhập thất bại');
}
}
}
async function logOut() {
await signOut(auth);
console.log('Người dùng đã đăng xuất');
}
Đăng nhập bằng 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('Đăng nhập đã bị hủy');
}
throw new Error('Đăng nhập bằng Google thất bại');
}
}
Bước 5: Bảo vệ các tuyến đường với trạng thái xác thực
import { onAuthStateChanged } from 'firebase/auth';
onAuthStateChanged(auth, (user) => {
if (user) {
console.log('Người dùng:', user.email);
window.location.href = '/dashboard';
} else {
console.log('Không có người dùng nào');
window.location.href = '/login';
}
});
Các lỗi xác thực thường gặp
Lỗi 1: Không xử lý làm mới mã thông báo
Firebase SDK tự động làm mới mã thông báo, nhưng mã phía máy chủ hết hạn sau 1 giờ. Luôn xác minh mã thông báo trên mỗi request hoặc triển khai logic làm mới.
Lỗi 2: Để lộ thông tin đăng nhập quản trị viên trong mã client
Không bao giờ sử dụng khóa tài khoản dịch vụ trong các ứng dụng client.
Lỗi 3: Bỏ qua xác minh email
import { sendEmailVerification } from 'firebase/auth';
async function sendVerificationEmail(user) {
await sendEmailVerification(user);
console.log('Email xác minh đã được gửi');
}
if (!auth.currentUser.emailVerified) {
console.log('Email chưa được xác minh');
// Hạn chế quyền truy cập
}
Cơ sở dữ liệu Firestore: Các thao tác và Truy vấn
Firestore là cơ sở dữ liệu NoSQL của Firebase. Các tài liệu được tổ chức thành các bộ sưu tập. Truy vấn tự động mở rộng.
Cấu trúc dữ liệu
your-project (gốc)
└── users (bộ sưu tập)
├── userId123 (tài liệu)
│ ├── name: "John"
│ ├── email: "john@example.com"
│ └── posts (bộ sưu tập con)
│ ├── postId1 (tài liệu)
│ └── postId2 (tài liệu)
└── userId456 (tài liệu)
Khởi tạo Firestore
import { getFirestore } from 'firebase/firestore';
const db = getFirestore(app);
Tạo tài liệu
import {
collection,
addDoc,
setDoc,
doc
} from 'firebase/firestore';
// Tùy chọn 1: ID tự động tạo
async function createUser(userData) {
const docRef = await addDoc(collection(db, 'users'), userData);
console.log('Tài liệu được ghi với ID:', docRef.id);
return docRef.id;
}
// Tùy chọn 2: ID tùy chỉnh
async function createUserWithId(userId, userData) {
await setDoc(doc(db, 'users', userId), userData);
console.log('Tài liệu được ghi với ID tùy chỉnh:', userId);
}
// Cách sử dụng
const userId = await createUser({
name: 'Alice',
email: 'alice@example.com',
createdAt: new Date(),
role: 'user'
});
Đọc tài liệu
import {
getDoc,
getDocs,
query,
where,
orderBy,
limit
} from 'firebase/firestore';
// Lấy một tài liệu
async function getUser(userId) {
const docRef = doc(db, 'users', userId);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
return docSnap.data();
} else {
throw new Error('Không tìm thấy người dùng');
}
}
// Truy vấn với bộ lọc
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;
}
// Cách sử dụng
const adminUsers = await getUsersByRole('admin');
console.log('Người dùng quản trị:', adminUsers);
Cập nhật tài liệu
import {
updateDoc,
increment,
arrayUnion,
arrayRemove
} from 'firebase/firestore';
async function updateUser(userId, updates) {
const userRef = doc(db, 'users', userId);
await updateDoc(userRef, updates);
}
// Các thao tác nguyên tử
await updateUser('userId123', {
loginCount: increment(1),
tags: arrayUnion('premium', 'beta-tester'),
lastLogin: new Date()
});
// Xóa khỏi mảng
await updateUser('userId123', {
tags: arrayRemove('beta-tester')
});
Xóa tài liệu
import { deleteDoc } from 'firebase/firestore';
async function deleteUser(userId) {
await deleteDoc(doc(db, 'users', userId));
console.log('Người dùng đã bị xóa');
}
Trình lắng nghe thời gian thực
import { onSnapshot } from 'firebase/firestore';
// Lắng nghe một tài liệu
const unsubscribe = onSnapshot(
doc(db, 'users', userId),
(doc) => {
console.log('Người dùng đã được cập nhật:', doc.data());
},
(error) => {
console.error('Lỗi lắng nghe:', error);
}
);
// Lắng nghe kết quả truy vấn
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('Bài viết đã xuất bản:', posts);
});
// Ngừng lắng nghe
unsubscribe();
unsubscribeQuery();
Quy tắc bảo mật Firestore
Đặt quy tắc trong Firebase Console > Firestore > Quy tắc:
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);
}
}
}
Hạn chế về truy vấn
Firestore có những ràng buộc:
-
Không có truy vấn OR (dùng
invới mảng hoặc nhiều truy vấn) - Không có tìm kiếm ký tự đại diện (dùng Algolia hoặc Meilisearch cho tìm kiếm toàn văn bản)
- Các truy vấn kết hợp cần chỉ mục
-
Giới hạn 30 disjunctions trong các truy vấn
in
Khắc phục cho truy vấn OR:
// where('status', '==', 'active') OR where('status', '==', 'pending')
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)
]);
// Hợp nhất kết quả trong client
Cloud Functions: Logic Backend phi máy chủ
Cloud Functions chạy mã backend mà không cần quản lý máy chủ. Kích hoạt theo thay đổi cơ sở dữ liệu, HTTP, hoặc các sự kiện đã lên lịch.
Thiết lập
# Cài đặt Firebase CLI
npm install -g firebase-tools
# Đăng nhập
firebase login
# Khởi tạo functions trong dự án
firebase init functions
# Chọn: JavaScript, ESLint yes, Express.js no
Hàm HTTP (Điểm cuối API)
// 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(doc => doc.data());
res.json({ success: true, data });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Điểm cuối được bảo vệ
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: 'Không được ủy quyền' });
}
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: 'Không tìm thấy người dùng' });
}
res.json({
success: true,
data: { id: userId, ...userDoc.data() }
});
} catch (error) {
res.status(401).json({ error: 'Mã thông báo không hợp lệ' });
}
});
Triển khai:
firebase deploy --only functions:getUserProfile
Gọi từ client:
async function getUserProfile(token) {
const response = await fetch(
'https://us-central1-your-app.cloudfunctions.net/getUserProfile',
{
headers: {
'Authorization': `Bearer ${token}`
}
}
);
const data = await response.json();
return data;
}
Kích hoạt cơ sở dữ liệu
const { onDocumentWritten } = require('firebase-functions/v2/firestore');
// Kích hoạt khi tài liệu người dùng thay đổi
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(`Email người dùng ${userId} đã thay đổi: ${before?.email} → ${after?.email}`);
await admin.auth().getUser(userId);
// Thêm logic email ở đây
}
}
);
// Kích hoạt khi tạo bài viết mới
exports.onNewPost = onDocumentWritten(
'posts/{postId}',
async (event) => {
const post = event.data?.after?.data();
if (!post) return;
if (!event.data?.before?.exists) {
console.log('Bài viết mới được tạo:', 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();
}
}
);
Hàm được lên lịch (Cron Jobs)
const { onSchedule } = require('firebase-functions/v2/scheduler');
// Chạy mỗi ngày vào nửa đêm UTC
exports.dailyCleanup = onSchedule('every 24 hours', async (event) => {
console.log('Đang chạy dọn dẹp hàng ngày');
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(`Đã xóa ${oldNotifs.size} thông báo cũ`);
});
Cấu hình môi trường
# Đặt biến môi trường
firebase functions:config:set \
stripe.secret="sk_test_xxx" \
email.api_key="key_xxx"
# Truy cập trong functions
const config = require('firebase-functions/config');
const stripe = require('stripe')(config.stripe.secret);
Cloud Storage: Tải lên và Quản lý tệp
Lưu trữ các tệp tải lên của người dùng, hình ảnh và tệp với phân phối CDN tự động.
Thiết lập Quy tắc Storage
// 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;
}
}
}
Tải lên tệp (Client)
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(`Tải lên: ${progress.toFixed(0)}%`);
},
(error) => {
switch (error.code) {
case 'storage/unauthorized':
reject(new Error('Bạn không có quyền'));
break;
case 'storage/canceled':
reject(new Error('Tải lên đã bị hủy'));
break;
default:
reject(new Error('Tải lên thất bại'));
}
},
async () => {
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
console.log('Tệp có sẵn tại:', downloadURL);
resolve(downloadURL);
}
);
});
}
// Cách sử dụng
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
});
}
Tải xuống tệp
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;
}
}
Xóa tệp
import { deleteObject } from 'firebase/storage';
async function deleteProfileImage(userId) {
const imageRef = ref(storage, `users/${userId}/profile/avatar.png`);
await deleteObject(imageRef);
console.log('Ảnh đại diện đã bị xóa');
}
Kiểm thử API Firebase với Apidog
Firebase cung cấp các API REST cho tất cả dịch vụ. Kiểm thử giúp gỡ lỗi và hiểu các yêu cầu.
Nhập API REST Firebase
- Mở Apidog
- Tạo dự án mới: “Firebase API”
- Nhập OpenAPI spec từ tài liệu Firebase
- Hoặc thêm điểm cuối thủ công
Điểm cuối REST Firestore:
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 }
}
}
Điểm cuối xác thực:
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123",
"returnSecureToken": true
}
Kiểm thử luồng xác thực
- Tạo yêu cầu: “Đăng nhập”
- Đặt phương thức: POST
- Thêm email/mật khẩu vào body
- Lưu mã thông báo phản hồi vào biến môi trường
- Sử dụng
{{token}}trong các request tiếp theo
Gỡ lỗi quy tắc bảo mật
Sử dụng Firebase Emulator Suite để kiểm thử cục bộ:
# Khởi động emulator
firebase emulators:start
# Kiểm thử với Firestore cục bộ
# http://localhost:8080
Các Thực hành Tốt nhất trong Sản xuất
1. Triển khai Xử lý lỗi thích hợp
// Logic thử lại cho các lỗi tạm thời
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. Tối ưu hóa hiệu suất truy vấn
Thêm chỉ mục tổng hợp cho các truy vấn đa trường:
const q = query(
collection(db, 'posts'),
where('category', '==', 'tech'),
where('views', '>', 1000),
orderBy('views', 'desc')
);
3. Các thao tác hàng loạt
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(`Đã cập nhật ${userIds.length} người dùng`);
}
// Tối đa 500 thao tác mỗi batch
4. Giám sát chi phí
Giá Firebase:
| Dịch vụ | Gói miễn phí | Trả phí |
|---|---|---|
| Firestore | 50K lượt đọc/ngày | $0.036/100K lượt đọc |
| Storage | 5GB | $0.023/GB |
| Functions | 2M lượt gọi | $0.40/1M |
| Auth | 10K/tháng | $0.0055/100K |
Đặt cảnh báo ngân sách trong Google Cloud Console.
5. Bảo mật tài khoản dịch vụ
// SAI: Không làm trong client
admin.initializeApp({
credential: admin.credential.cert(require('./serviceAccountKey.json'))
});
// ĐÚNG: Chỉ dùng môi trường server
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
6. Xử lý các kịch bản ngoại tuyến
// Bật tính năng bền vững ngoại tuyến (web)
import { enableMultiTabIndexedDbPersistence } from 'firebase/firestore';
enableMultiTabIndexedDbPersistence(db)
.catch((err) => {
if (err.code === 'failed-precondition') {
// Nhiều tab đang mở
} else if (err.code === 'unimplemented') {
// Trình duyệt không hỗ trợ
}
});
// Lắng nghe kết nối
import { onSnapshot } from 'firebase/firestore';
onSnapshot(doc(db, 'status', 'online'), (doc) => {
if (!doc.exists()) {
console.log('Bạn đang ngoại tuyến');
// Hiển thị giao diện ngoại tuyến
}
});
Các vấn đề và Giải pháp API Firebase Thường gặp
Vấn đề 1: Lỗi từ chối quyền
Triệu chứng: Lỗi: 7 PERMISSION_DENIED
Nguyên nhân: Quy tắc bảo mật chặn thao tác
Cách khắc phục:
- Kiểm tra quy tắc trong Firebase Console
- Xác minh
request.auth.uidkhớp với người dùng mong muốn - Kiểm thử quy tắc bằng Rules Playground
Vấn đề 2: Mã thông báo hết hạn
Triệu chứng: Lỗi: Mã thông báo ID đã hết hạn
Cách khắc phục:
// Buộc làm mới mã thông báo
const user = auth.currentUser;
if (user) {
await user.getIdToken(true); // Buộc làm mới
}
Vấn đề 3: Độ trễ khởi động nguội
Triệu chứng: Cloud Functions mất 2-5 giây lần đầu
Cách khắc phục:
// Ping giữ ấm function
exports.keepWarm = onSchedule('every 60 seconds', async () => {
await fetch('https://your-function.cloudfunctions.net/health');
});
Vấn đề 4: Truy vấn trả về kết quả trống
Triệu chứng: Truy vấn trả mảng trống
Nguyên nhân: Thiếu chỉ mục hoặc thứ tự trường sai
Cách khắc phục: Kiểm tra Firestore Console > Indexes để tạo chỉ mục cần thiết.
Các trường hợp sử dụng thực tế
Ứng dụng Fintech: Cập nhật giao dịch thời gian thực
Một startup thanh toán sử dụng Firestore để thông báo giao dịch thời gian thực. Cloud Functions đẩy cập nhật đến tất cả dashboard admin trong ~200ms. Kết quả: giảm 40% yêu cầu hỗ trợ giao dịch "đang chờ".
Thương mại điện tử: Đồng bộ hóa kho hàng
Nhà bán lẻ đồng bộ kho trên web/iOS/Android bằng trình lắng nghe Firestore. Khi kho thay đổi, mọi client đều nhận cập nhật. Hỗ trợ offline đảm bảo nhân viên kho quét mã mà không cần mạng và tự động đồng bộ lại khi kết nối.
SaaS: Xác thực đa khách thuê
Nền tảng B2B dùng Firebase Auth với custom claims cho quyền truy cập dựa trên vai trò. Cloud Functions kiểm soát quyền admin qua cấu hình Firestore. Một codebase phục vụ 500+ tổ chức, dữ liệu được cô lập.
Kết luận
Tích hợp API Firebase gồm 4 dịch vụ cốt lõi:
- Xác thực: Đăng nhập Email, Google, Apple với JWT
- Firestore: NoSQL với lắng nghe realtime và quy tắc bảo mật
- Cloud Functions: Backend phi máy chủ kích hoạt bởi sự kiện/HTTP
- Storage: Tải lên tệp và phân phối CDN
Bạn đã thấy các luồng xác thực, thao tác database, triển khai function, quản lý tệp, mẫu production: xử lý lỗi, batch, offline, bảo mật.
FAQ
Firebase có miễn phí để sử dụng không?
Có, Firebase có một gói miễn phí hào phóng (Spark Plan) bao gồm 5GB lưu trữ, 50K lượt đọc Firestore/ngày, 2M lượt gọi Cloud Function và 10K người dùng Auth/tháng. Các gói trả phí (Blaze) sử dụng giá trả theo mức sử dụng.
Tôi có thể sử dụng Firebase với các cơ sở dữ liệu hiện có không?
Có. Sử dụng Firebase Extensions để đồng bộ hóa với PostgreSQL, MySQL hoặc MongoDB. Hoặc gọi các API bên ngoài từ Cloud Functions để tích hợp với các hệ thống hiện có.
Làm cách nào để di chuyển từ Firebase sang nền tảng khác?
Xuất dữ liệu bằng chức năng export của Firestore hoặc Firebase CLI. Với tập dữ liệu lớn, dùng Dataflow export. Độ phức tạp di chuyển phụ thuộc cấu trúc dữ liệu.
Firebase có hỗ trợ GraphQL không?
Không native. Có thể dùng bên thứ ba như firestore-graphql hoặc tự build một lớp GraphQL với Cloud Functions + Apollo Server.
Tôi có thể sử dụng Firebase tại chỗ không?
Không. Firebase chỉ chạy trên Google Cloud. Nếu cần tự lưu trữ, cân nhắc Appwrite, Supabase hoặc Nhost.
Làm cách nào để xử lý các tệp tải lên lớn hơn 100MB?
Dùng upload resumable (chunking). SDK Firebase tự xử lý. Với tệp rất lớn, dùng Google Cloud Storage trực tiếp với signed URLs.
Điều gì xảy ra nếu vượt quá giới hạn truy vấn Firestore?
Truy vấn sẽ lỗi FAILED_PRECONDITION. Thêm chỉ mục cần thiết hoặc cấu trúc lại truy vấn. Firestore cung cấp link tạo chỉ mục thiếu trong thông báo lỗi.
Firebase có tuân thủ GDPR không?
Có, Firebase cung cấp khả năng xử lý dữ liệu tuân thủ GDPR. Bật lưu trữ dữ liệu tại khu vực cụ thể, xuất/xóa dữ liệu người dùng, ký Thỏa thuận Xử lý Dữ liệu với Google.




Top comments (0)