أنت تبني تطبيقًا. يحتاج المستخدمون إلى تسجيل الدخول. يجب مزامنة البيانات في الوقت الفعلي. تحتاج الملفات إلى التخزين. يمكنك إطلاق خوادم، وتكوين قواعد بيانات، وإدارة البنية التحتية لأسابيع. أو يمكنك استخدام Firebase.
تدعم Firebase أكثر من 1.5 مليون تطبيق، بما في ذلك The New York Times، وDuolingo، وعلي بابا. يختارها المطورون لأنها تزيل تعقيد الواجهة الخلفية. أنت تركز على الميزات، وليس صيانة الخادم. ولكن واجهة برمجة تطبيقات Firebase لها بعض الغرابة. تدفقات المصادقة تربك المبتدئين. قواعد البيانات تربك المطورين ذوي الخبرة. تبدو وظائف Cloud Functions سحرية حتى تفهم المشغلات.
لقد قمت بدمج Firebase في تطبيقات إنتاج تخدم ملايين المستخدمين. لقد ارتكبت كل الأخطاء الممكنة: مفاتيح حساب الخدمة المكشوفة، استعلامات غير فعالة، وظائف معطلة. هذا الدليل يستخلص تلك الدروس.
ستتعلم المصادقة، وعمليات قواعد البيانات، ووظائف Cloud Functions، والتخزين. سترى كودًا عمليًا، وليس مجرد نظرية. ستتجنب المزالق التي تسبب مشكلات في الإنتاج.
ما هي واجهة برمجة تطبيقات Firebase ولماذا هي مهمة؟
Firebase ليست واجهة برمجة تطبيقات واحدة. إنها مجموعة من خدمات الواجهة الخلفية التي يتم الوصول إليها من خلال مجموعات SDKs موحدة ونقاط نهاية REST.
خدمات Firebase الأساسية
| الخدمة | الغرض | نوع واجهة برمجة التطبيقات (API) |
|---|---|---|
| المصادقة | تسجيل دخول المستخدم والهوية | SDK + REST |
| قاعدة بيانات Firestore | قاعدة بيانات مستندات NoSQL | SDK + REST |
| قاعدة بيانات Realtime | مزامنة JSON في الوقت الفعلي | SDK + REST |
| التخزين السحابي (Cloud Storage) | تخزين الملفات وCDN | SDK + REST |
| وظائف Cloud Functions | الحوسبة بلا خادم (Serverless) | واجهة سطر الأوامر (Deployment CLI) |
| الاستضافة (Hosting) | استضافة الويب الثابتة | واجهة سطر الأوامر (Deployment CLI) |
| الرسائل السحابية (Cloud Messaging) | إشعارات الدفع (Push notifications) | HTTP v1 API |
متى يكون Firebase خيارًا منطقيًا
يحل Firebase مشاكل محددة بكفاءة:
استخدم Firebase عندما:
- تحتاج إلى مزامنة في الوقت الفعلي (الدردشة، التعاون، التحديثات المباشرة)
- ترغب في بنية بدون خادم (لا إدارة للبنية التحتية)
- تقوم ببناء تطبيقات جوال أو ويب (مجموعات SDKs تتعامل مع اختلافات المنصات)
- تحتاج إلى دعم عدم الاتصال بالإنترنت (مجموعات SDKs تخزن البيانات مؤقتًا تلقائيًا)
- ترغب في مصادقة مدمجة (تسجيل الدخول عبر Google، Apple، البريد الإلكتروني، الهاتف)
تجنب Firebase عندما:
- تحتاج إلى استعلامات علائقية معقدة (استخدم PostgreSQL بدلاً من ذلك)
- لديك متطلبات صارمة لخصوصية البيانات (مناطق Firebase محدودة)
- تحتاج إلى قدرات SQL كاملة (توجد قيود على استعلامات Firestore)
- تكون التكلفة على نطاق واسع أهم من سرعة التطوير (الاستضافة الذاتية أرخص)
هندسة واجهة برمجة تطبيقات Firebase
تستخدم Firebase نهجًا هجينًا:
┌─────────────────────────────────────────────────────────┐
│ تطبيقك │
├─────────────────────────────────────────────────────────┤
│ Firebase SDK (العميل) │
│ - يعالج رموز المصادقة تلقائيًا │
│ - يدير التخزين المؤقت في وضع عدم الاتصال │
│ - مستمعو الوقت الفعلي │
└─────────────────────────────────────────────────────────┘
│
│ HTTPS + WebSocket
▼
┌─────────────────────────────────────────────────────────┐
│ واجهة Firebase الخلفية │
├──────────────┬──────────────┬──────────────┬────────────┤
│ خدمة │ قاعدة بيانات│ خدمة │ بيئة تشغيل│
│ المصادقة │ Firestore │ التخزين │ الوظائف │
└──────────────┴──────────────┴──────────────┴────────────┘
تعمل مجموعات SDKs للعميل على تجريد طبقة HTTP. تحت الغطاء، تُترجم كل عملية إلى استدعاءات REST API مع مصادقة JWT.
مصادقة Firebase: إعداد كامل
المصادقة هي أول عملية دمج لك مع Firebase. إذا أخطأت في هذا، فكل شيء آخر سيفشل.
الخطوة 1: إنشاء مشروع Firebase
- اذهب إلى وحدة تحكم Firebase

انقر على "إضافة مشروع" وأدخل اسم المشروع (بدون مسافات)

تفعيل Google Analytics (اختياري لكن موصى به)

انقر على "إنشاء مشروع"

انتظر 30 ثانية للتزويد. ستظهر لك لوحة تحكم المشروع.
الخطوة 2: تسجيل تطبيقك
لتطبيقات الويب:
// في Firebase Console > Project Settings > General
// انقر على "إضافة تطبيق" > أيقونة الويب
// تسجيل تطبيق الويب
const firebaseConfig = {
apiKey: "AIzaSyDxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "your-app.firebaseapp.com",
projectId: "your-app",
storageBucket: "your-app.appspot.com",
messagingSenderId: "123456789012",
appId: "1:123456789012:web:abc123def456"
};
// تهيئة Firebase
import { initializeApp } from 'firebase/app';
const app = initializeApp(firebaseConfig);
لتطبيقات iOS:
قم بتنزيل GoogleService-Info.plist وأضفه إلى مشروع Xcode. تأكد من أن "Target Membership" يتضمن تطبيقك.
لتطبيقات Android:
قم بتنزيل google-services.json وضعه في مجلد app/. أضفه إلى build.gradle:
// 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'
}
الخطوة 3: تمكين طرق المصادقة
في وحدة تحكم Firebase > المصادقة > طريقة تسجيل الدخول:
- البريد الإلكتروني/كلمة المرور: قم بتمكينها للتسجيل التقليدي
- Google: أضف بصمة شهادة SHA-1 الخاصة بك (Android) أو معرّف الحزمة (bundle ID) الخاص بك (iOS)
- Apple: مطلوب لتطبيقات iOS إذا قمت بتمكين أي تسجيل دخول اجتماعي
- الهاتف: قم بتمكينه للمصادقة عبر الرسائل القصيرة (يتطلب فاتورة)
الخطوة 4: تنفيذ تدفق المصادقة
التسجيل بالبريد الإلكتروني/كلمة المرور:
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: displayName
});
console.log('تم إنشاء المستخدم:', userCredential.user.uid);
return userCredential.user;
} catch (error) {
// التعامل مع رموز الأخطاء المحددة
switch (error.code) {
case 'auth/email-already-in-use':
throw new Error('هذا البريد الإلكتروني مسجل بالفعل');
case 'auth/weak-password':
throw new Error('يجب أن تتكون كلمة المرور من 6 أحرف على الأقل');
case 'auth/invalid-email':
throw new Error('عنوان بريد إلكتروني غير صالح');
default:
throw new Error('فشل التسجيل: ' + error.message);
}
}
}
تسجيل الدخول بالبريد الإلكتروني/كلمة المرور:
import {
signInWithEmailAndPassword,
signOut
} from 'firebase/auth';
async function signIn(email, password) {
try {
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
const user = userCredential.user;
// الحصول على رمز الهوية لمكالمات API
const idToken = await user.getIdToken();
console.log('رمز المصادقة:', idToken);
return user;
} catch (error) {
switch (error.code) {
case 'auth/user-not-found':
throw new Error('لا يوجد حساب بهذا البريد الإلكتروني');
case 'auth/wrong-password':
throw new Error('كلمة مرور غير صحيحة');
case 'auth/too-many-requests':
throw new Error('محاولات كثيرة جدًا. حاول مرة أخرى لاحقًا');
default:
throw new Error('فشل تسجيل الدخول');
}
}
}
async function logOut() {
await signOut(auth);
console.log('تم تسجيل خروج المستخدم');
}
تسجيل الدخول عبر Google (الويب):
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;
// الوصول إلى رمز OAuth من Google
const credential = GoogleAuthProvider.credentialFromResult(result);
const googleAccessToken = credential.accessToken;
return user;
} catch (error) {
if (error.code === 'auth/popup-closed-by-user') {
throw new Error('تم إلغاء تسجيل الدخول');
}
throw new Error('فشل تسجيل الدخول عبر Google');
}
}
الخطوة 5: حماية المسارات بحالة المصادقة
import { onAuthStateChanged } from 'firebase/auth';
// الاشتراك في تغييرات حالة المصادقة
onAuthStateChanged(auth, (user) => {
if (user) {
// المستخدم مسجل الدخول
console.log('المستخدم:', user.email);
// إعادة التوجيه إلى لوحة التحكم
window.location.href = '/dashboard';
} else {
// المستخدم غير مسجل الدخول
console.log('لا يوجد مستخدم');
// إعادة التوجيه إلى صفحة تسجيل الدخول
window.location.href = '/login';
}
});
أخطاء المصادقة الشائعة
الخطأ 1: عدم التعامل مع تحديث الرمز المميز (token refresh)
يقوم Firebase SDK بتحديث الرموز المميزة تلقائيًا. ولكن إذا قمت بتخزين الرموز المميزة مؤقتًا في جانب الخادم، فإنها تنتهي صلاحيتها بعد ساعة واحدة. قم دائمًا بالتحقق من الرموز المميزة في كل طلب أو قم بتنفيذ منطق التحديث.
الخطأ 2: كشف بيانات اعتماد المشرف في كود العميل
لا تستخدم أبدًا مفاتيح حساب الخدمة في تطبيقات العميل. تتجاوز حسابات الخدمة قواعد الأمان. استخدمها فقط في بيئات الخادم الموثوقة.
الخطأ 3: تخطي التحقق من البريد الإلكتروني
import { sendEmailVerification } from 'firebase/auth';
async function sendVerificationEmail(user) {
await sendEmailVerification(user);
console.log('تم إرسال بريد التحقق');
}
// التحقق من حالة التحقق
if (!auth.currentUser.emailVerified) {
console.log('البريد الإلكتروني غير متحقق');
// تقييد الوصول
}
قاعدة بيانات Firestore: العمليات والاستعلامات
Firestore هي قاعدة بيانات NoSQL من Firebase. تُنظّم المستندات في مجموعات. تتوسع الاستعلامات تلقائيًا.
هيكل البيانات
your-project (الجذر)
└── users (مجموعة)
├── userId123 (مستند)
│ ├── name: "John"
│ ├── email: "john@example.com"
│ └── posts (مجموعة فرعية)
│ ├── postId1 (مستند)
│ └── postId2 (مستند)
└── userId456 (مستند)
تهيئة Firestore
import { getFirestore } from 'firebase/firestore';
const db = getFirestore(app);
إنشاء المستندات
import {
collection,
addDoc,
setDoc,
doc
} from 'firebase/firestore';
// الخيار 1: معرف يتم إنشاؤه تلقائيًا
async function createUser(userData) {
const docRef = await addDoc(collection(db, 'users'), userData);
console.log('تم كتابة المستند بالمعرف:', docRef.id);
return docRef.id;
}
// الخيار 2: معرف مخصص
async function createUserWithId(userId, userData) {
await setDoc(doc(db, 'users', userId), userData);
console.log('تم كتابة المستند بمعرف مخصص:', userId);
}
// الاستخدام
const userId = await createUser({
name: 'Alice',
email: 'alice@example.com',
createdAt: new Date(),
role: 'user'
});
قراءة المستندات
import {
getDoc,
getDocs,
query,
where,
orderBy,
limit
} from 'firebase/firestore';
// الحصول على مستند واحد
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('المستخدم غير موجود');
}
}
// استعلام مع الفلاتر
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;
}
// الاستخدام
const adminUsers = await getUsersByRole('admin');
console.log('المستخدمون المشرفون:', adminUsers);
تحديث المستندات
import {
updateDoc,
increment,
arrayUnion,
arrayRemove
} 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')
});
حذف المستندات
import { deleteDoc } from 'firebase/firestore';
async function deleteUser(userId) {
await deleteDoc(doc(db, 'users', userId));
console.log('تم حذف المستخدم');
}
مستمعو الوقت الفعلي
import { onSnapshot } from 'firebase/firestore';
// الاستماع إلى مستند واحد
const unsubscribe = onSnapshot(
doc(db, 'users', userId),
(doc) => {
console.log('تم تحديث المستخدم:', doc.data());
},
(error) => {
console.error('خطأ في الاستماع:', error);
}
);
// الاستماع إلى نتائج الاستعلام
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('المنشورات المنشورة:', posts);
});
// إيقاف الاستماع
unsubscribe();
unsubscribeQuery();
قواعد أمان Firestore
بدون قواعد مناسبة، يمكن لأي شخص قراءة بياناتك. قم بتعيين القواعد في Firebase Console > Firestore > 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 لديه قيود:
- لا توجد استعلامات OR (استخدم
inمع مصفوفة أو استعلامات متعددة) - لا توجد عمليات بحث بأحرف البدل (استخدم Algolia أو Meilisearch للبحث بالنص الكامل)
- الاستعلامات المركبة تحتاج إلى فهارس (Firestore ينشئ تلقائيًا فهارس أحادية الحقل)
- الحد الأقصى 30 disjunctions في استعلامات
in
حل بديل لاستعلامات 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)
]);
// دمج النتائج في العميل
وظائف Cloud Functions: منطق الواجهة الخلفية بلا خادم
تُشغل وظائف Cloud Functions كود الواجهة الخلفية دون إدارة الخوادم. يتم تشغيلها بناءً على تغييرات قاعدة البيانات، طلبات HTTP، أو الأحداث المجدولة.
الإعداد
# تثبيت Firebase CLI
npm install -g firebase-tools
# تسجيل الدخول
firebase login
# تهيئة الوظائف في مشروعك
firebase init functions
# اختر: JavaScript, ESLint نعم, Express.js لا
وظائف HTTP (نقاط نهاية 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 });
}
});
// نقطة نهاية محمية (التحقق من رمز المصادقة)
exports.getUserProfile = onRequest(async (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
// الحصول على الرمز المميز من رأس Authorization
const authHeader = req.headers.authorization || '';
const token = authHeader.split('Bearer ')[1];
if (!token) {
return res.status(401).json({ error: 'غير مصرح به' });
}
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: 'المستخدم غير موجود' });
}
res.json({
success: true,
data: { id: userId, ...userDoc.data() }
});
} catch (error) {
res.status(401).json({ error: 'رمز مميز غير صالح' });
}
});
النشر:
firebase deploy --only functions:getUserProfile
الاستدعاء من العميل:
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;
}
مشغلات قاعدة البيانات
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(`تم تغيير بريد المستخدم ${userId}: ${before?.email} ← ${after?.email}`);
// إرسال بريد إلكتروني للإشعار
await admin.auth().getUser(userId);
// أضف منطق بريدك الإلكتروني هنا
}
}
);
// التشغيل عند إنشاء منشور جديد
exports.onNewPost = onDocumentWritten(
'posts/{postId}',
async (event) => {
const post = event.data?.after?.data();
if (!post) return; // تم حذف المستند
// التحقق مما إذا كان هذا مستندًا جديدًا
if (!event.data?.before?.exists) {
console.log('تم إنشاء منشور جديد:', 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();
}
}
);
الوظائف المجدولة (مهام Cron)
const { onSchedule } = require('firebase-functions/v2/scheduler');
// تشغيل كل يوم عند منتصف الليل بالتوقيت العالمي المنسق (UTC)
exports.dailyCleanup = onSchedule('every 24 hours', async (event) => {
console.log('تشغيل عملية التنظيف اليومية');
// حذف الإشعارات القديمة (أكثر من 30 يومًا)
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(`تم حذف ${oldNotifs.size} إشعارات قديمة`);
});
تهيئة البيئة
# تعيين متغيرات البيئة
firebase functions:config:set \
stripe.secret="sk_test_xxx" \
email.api_key="key_xxx"
# الوصول في الوظائف
const config = require('firebase-functions/config');
const stripe = require('stripe')(config.stripe.secret);
التخزين السحابي: تحميل الملفات وإدارتها
قم بتخزين ملفات المستخدمين المحملة والصور والملفات مع التوزيع التلقائي عبر شبكة CDN.
إعداد قواعد التخزين
// 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; // للمشرفين فقط عبر Firebase Console
}
}
}
تحميل الملفات (العميل)
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(`التحميل: ${progress.toFixed(0)}%`);
},
(error) => {
// معالجة الأخطاء
switch (error.code) {
case 'storage/unauthorized':
reject(new Error('ليس لديك إذن'));
break;
case 'storage/canceled':
reject(new Error('تم إلغاء التحميل'));
break;
default:
reject(new Error('فشل التحميل'));
}
},
async () => {
// اكتمل التحميل
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
console.log('الملف متاح على:', 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);
// حفظ الرابط إلى Firestore
await updateDoc(doc(db, 'users', auth.currentUser.uid), {
profileImage: imageUrl
});
}
تنزيل الملفات
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;
}
}
حذف الملفات
import { deleteObject } from 'firebase/storage';
async function deleteProfileImage(userId) {
const imageRef = ref(storage, `users/${userId}/profile/avatar.png`);
await deleteObject(imageRef);
console.log('تم حذف صورة الملف الشخصي');
}
اختبار واجهات برمجة تطبيقات Firebase باستخدام Apidog
توفر Firebase واجهات برمجة تطبيقات REST لجميع الخدمات. يساعد اختبارها مباشرة في تصحيح الأخطاء وفهم الطلبات الأساسية.
استيراد Firebase REST API
- افتح Apidog
- أنشئ مشروعًا جديدًا: "Firebase API"
- استورد مواصفات OpenAPI من وثائق Firebase
- أو أضف نقاط النهاية يدويًا:
نقطة نهاية Firestore REST:
POST https://firestore.googleapis.com/v1/projects/{projectId}/databases/(default)/documents
Authorization: Bearer {oauth2_token}
Content-Type: application/json
{
"fields": {
"name": { "stringValue": "John" },
"email": { "stringValue": "john@example.com" },
"age": { "integerValue": 30 }
}
}
نقطة نهاية المصادقة:
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123",
"returnSecureToken": true
}
اختبار تدفق المصادقة
- أنشئ طلبًا: "تسجيل الدخول"
- عيّن الطريقة: POST
- أضف البريد الإلكتروني/كلمة المرور في الجسم
- احفظ رمز الاستجابة كمتغير بيئة
- استخدم
{{token}}في الطلبات اللاحقة
تصحيح أخطاء قواعد الأمان
استخدم Firebase Emulator Suite للاختبار المحلي:
# تشغيل المحاكي
firebase emulators:start
# الاختبار مقابل Firestore المحلي
# http://localhost:8080
أفضل ممارسات الإنتاج
1. تنفيذ معالجة الأخطاء المناسبة
// منطق إعادة المحاولة للأعطال العابرة
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. تحسين أداء الاستعلام
أضف فهارس مركبة للاستعلامات متعددة الحقول:
// هذا الاستعلام يحتاج إلى فهرس مركب
const q = query(
collection(db, 'posts'),
where('category', '==', 'tech'),
where('views', '>', 1000),
orderBy('views', 'desc')
);
تطلب منك Firestore إنشاء الفهرس مع رابط مباشر عند تشغيل هذا الاستعلام.
3. عمليات الدفعة (Batch Operations)
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(`تم تحديث ${userIds.length} مستخدمًا`);
}
// الحد الأقصى 500 عملية في الدفعة الواحدة
4. مراقبة التكاليف
تسعير Firebase:
| الخدمة | الفئة المجانية | المدفوعة |
|---|---|---|
| Firestore | 50 ألف قراءة/يوم | 0.036 دولار/100 ألف قراءة |
| التخزين | 5 جيجابايت | 0.023 دولار/جيجابايت |
| الوظائف | 2 مليون استدعاء | 0.40 دولار/مليون |
| المصادقة | 10 آلاف/شهر | 0.0055 دولار/100 ألف |
عيّن تنبيهات الميزانية في Google Cloud Console.
5. تأمين حسابات الخدمة
// خطأ: لا تفعل هذا أبدًا في كود العميل
admin.initializeApp({
credential: admin.credential.cert(require('./serviceAccountKey.json'))
});
// صحيح: استخدم في بيئة الخادم فقط
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
6. التعامل مع سيناريوهات عدم الاتصال
// تمكين استمرارية وضع عدم الاتصال (الويب)
import { enableMultiTabIndexedDbPersistence } from 'firebase/firestore';
enableMultiTabIndexedDbPersistence(db)
.catch((err) => {
if (err.code === 'failed-precondition') {
// علامات تبويب متعددة مفتوحة
} else if (err.code === 'unimplemented') {
// المتصفح لا يدعم
}
});
// الاستماع إلى الاتصال بالشبكة
import { onSnapshot } from 'firebase/firestore';
onSnapshot(doc(db, 'status', 'online'), (doc) => {
if (!doc.exists()) {
console.log('أنت غير متصل بالإنترنت');
// عرض واجهة المستخدم لعدم الاتصال
}
});
مشكلات وحلول واجهة برمجة تطبيقات Firebase الشائعة
المشكلة 1: أخطاء رفض الإذن
العرض: Error: 7 PERMISSION_DENIED
السبب: قواعد الأمان تمنع العملية
الحل:
- تحقق من القواعد في Firebase Console
- تحقق مما إذا كان
request.auth.uidيطابق المستخدم المتوقع - اختبر القواعد باستخدام Rules Playground
المشكلة 2: انتهاء صلاحية الرمز المميز
العرض: Error: ID token expired
الحل:
// فرض تحديث الرمز المميز
const user = auth.currentUser;
if (user) {
await user.getIdToken(true); // فرض التحديث
}
المشكلة 3: تأخر بدء التشغيل البارد
العرض: تستغرق وظائف Cloud Functions من 2 إلى 5 ثوانٍ في أول استدعاء
الحل:
// حافظ على دفء الوظائف باستخدام عمليات ping المجدولة
exports.keepWarm = onSchedule('every 60 seconds', async () => {
await fetch('https://your-function.cloudfunctions.net/health');
});
المشكلة 4: الاستعلام يعيد نتائج فارغة
العرض: يجب أن يعيد الاستعلام بيانات ولكنه يعيد مصفوفة فارغة
السبب: فهرس مفقود أو ترتيب حقول خاطئ
الحل: تحقق من Firebase Console > Indexes بحثًا عن الفهارس المركبة المطلوبة.
حالات الاستخدام الواقعية
تطبيق التكنولوجيا المالية: تحديثات المعاملات في الوقت الفعلي
استخدمت شركة ناشئة في مجال المدفوعات Firebase Firestore لإنشاء إشعارات المعاملات في الوقت الفعلي. عندما تتم معالجة الدفعة، تقوم Cloud Functions بتشغيل تحديثات لجميع لوحات تحكم المشرفين المتصلة في غضون 200 مللي ثانية. النتيجة: انخفاض بنسبة 40٪ في تذاكر الدعم المتعلقة بالمعاملات "المعلقة".
التجارة الإلكترونية: مزامنة المخزون
يقوم بائع تجزئة عبر الإنترنت بمزامنة المخزون عبر الويب و iOS و Android باستخدام مستمعي Firestore. عندما يتغير المخزون، يتم تحديث جميع العملاء تلقائيًا. تضمن استمرارية وضع عدم الاتصال أن يتمكن عمال المستودعات من مسح العناصر ضوئيًا دون اتصال، مع المزامنة التلقائية عند إعادة الاتصال.
SaaS: مصادقة متعددة المستأجرين
تستخدم منصة B2B مصادقة Firebase مع مطالبات مخصصة للوصول المستند إلى الأدوار. يحصل المستخدمون المسؤولون على أذونات مرتفعة عبر Cloud Functions التي تتحقق من صحة تكوينات المستأجرين في Firestore. قاعدة بيانات واحدة تخدم أكثر من 500 مؤسسة ببيانات معزولة.
الخاتمة
يتضمن دمج Firebase API أربع خدمات أساسية:
- المصادقة: تسجيل الدخول بالبريد الإلكتروني، Google، Apple باستخدام رموز JWT
- Firestore: قاعدة بيانات NoSQL مع مستمعي الوقت الفعلي وقواعد الأمان
- Cloud Functions: واجهة خلفية بدون خادم يتم تشغيلها بواسطة الأحداث أو HTTP
- التخزين: تحميل الملفات مع توزيع CDN
لقد تعلمت تدفقات المصادقة، وعمليات قواعد البيانات، ونشر الوظائف، وإدارة الملفات. لقد رأيت أنماط الإنتاج: معالجة الأخطاء، المعالجة الدفعية، دعم عدم الاتصال، والأمان.
الأسئلة الشائعة
هل Firebase مجاني للاستخدام؟
نعم، لدى Firebase طبقة مجانية سخية (خطة Spark) تتضمن 5 جيجابايت من التخزين، 50 ألف قراءة يوميًا من Firestore، 2 مليون استدعاء لوظائف Cloud Functions، و 10 آلاف مستخدم للمصادقة شهريًا. تستخدم الخطط المدفوعة (Blaze) تسعير الدفع حسب الاستخدام.
هل يمكنني استخدام Firebase مع قواعد البيانات الموجودة؟
نعم. استخدم Firebase Extensions للمزامنة مع PostgreSQL، MySQL، أو MongoDB. أو قم باستدعاء واجهات برمجة تطبيقات خارجية من Cloud Functions للدمج مع الأنظمة الموجودة.
كيف يمكنني الترحيل من Firebase إلى منصة أخرى؟
قم بتصدير البيانات باستخدام وظائف تصدير Firestore أو Firebase CLI. لمجموعات البيانات الكبيرة، استخدم مسار تصدير Dataflow. تعتمد تعقيد عملية الترحيل على هيكل بياناتك.
هل Firebase يدعم GraphQL؟
ليس بشكل أساسي. استخدم حلولًا خارجية مثل firestore-graphql أو قم ببناء طبقة GraphQL باستخدام Cloud Functions و Apollo Server.
هل يمكنني استخدام Firebase محليًا؟
لا. Firebase يعمل فقط على Google Cloud. للبدائل المستضافة ذاتيًا، فكر في Appwrite، Supabase، أو Nhost.
كيف أتعامل مع تحميل الملفات التي يزيد حجمها عن 100 ميجابايت؟
استخدم التحميلات القابلة للاستئناف مع التقطيع. يتعامل Firebase SDK مع هذا تلقائيًا. للملفات الكبيرة جدًا، استخدم Google Cloud Storage مباشرة مع عناوين URL الموقعة.
ماذا يحدث إذا تجاوزت حدود استعلام Firestore؟
تفشل الاستعلامات بخطأ FAILED_PRECONDITION. أضف الفهارس المطلوبة أو أعد هيكلة الاستعلامات. توفر Firestore روابط مباشرة لإنشاء الفهارس المفقودة في رسالة الخطأ.
هل Firebase متوافق مع اللائحة العامة لحماية البيانات (GDPR)؟
نعم، تقدم Firebase معالجة بيانات متوافقة مع اللائحة العامة لحماية البيانات (GDPR). قم بتمكين مكان إقامة البيانات في مناطق محددة، وقم بتنفيذ تصدير/حذف بيانات المستخدم، ووقع على تعديل معالجة البيانات من Google.
