كيفية استخدام Firebase Firestore كقاعدة بيانات

برنامج تعليمي لإعداد Firestore كقاعدة بيانات ، وهو حل مناسب جدًا لمشاكل التخزين الخاصة بك!

كنت بحاجة إلى إنشاء تخزين لبعض البيانات الخاصة بيعضوية النادي، المكان الذي أقوم فيه بتدريس البرمجة.

أردت أن يتمكن المستخدمون من قول "لقد أكملت هذه الدورة التدريبية" يدويًا بالنقر فوق الزر.

في الأساس ، كنت أرغب في تخزين كائن معين لكل مستخدم.

إعداد Firebase

قررت استخدامFirebaseلهذا ، وعلى وجه الخصوصقاعدة بيانات Firestoreانهم يقدموا.

الطبقة المجانية الخاصة به سخية ، مع ما يصل إلى 1 جيجابايت من البيانات المخزنة و 10 جيجابايت من نقل الشبكة شهريًا. طريقة تفوق تقديراتي لما احتاجه!

افتح موقع Firebase الإلكتروني علىhttps://firebase.google.com/

Firebase هو أحد منتجات Google ، لذلك بمجرد تسجيل الدخول إلى Google ، يتم تسجيل دخولك أيضًا إلى Firebase.

أنشأت مشروع Firebase جديدًا بالنقر على "إنشاء مشروع"

أعطيته اسمًا:

وأنه كان عليه:

نقرت على أيقونة "الويب" بجوار iOS و Android ، وأدخلت اسم التطبيق:

وأعطاني Firebase على الفور مفاتيح الوصول التي أحتاجها ، بالإضافة إلى بعض التعليمات البرمجية النموذجية:

بعد ذلك مباشرة ، طلب مني Firebase إضافة بعض قواعد الأمان لقاعدة البيانات.

يمكنك اختيار شيئين بشكل افتراضي: مفتوح للجميع ، أو مغلق للجميع. بدأت مفتوحة للجميع ، شيء يسمونهوضع الاختبار.

هذا هو! كنت على استعداد للذهاب من خلال إنشاء مجموعة.

ما هي المجموعة؟ في مصطلحات Firestore ، يمكننا إنشاء العديد من المجموعات المختلفة وتعيين المستندات لكل مجموعة.

يمكن أن يحتوي المستند بعد ذلك على حقول ومجموعات أخرى.

إنها لا تختلف كثيرًا عن قواعد بيانات NoSQL الأخرى ، مثلMongoDB.

أوصي بشدة بالمشاهدةقائمة تشغيل YouTube حول هذا الموضوع، إنه عمل جيد جدًا.

لذلك أضفت مجموعتي التي اتصلت بهاusers.

أردت تحديد كل مستخدم باستخدام سلسلة خاصة أسميهاid.

كود الواجهة الأمامية

نصل الآن إلى جزء JavaScript.

في التذييل ، قمت بتضمين هذين الملفين المقدمين من Firebase:

<script src="https://www.gstatic.com/
firebasejs/7.2.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/
firebasejs/7.2.1/firebase-firestore.js"></script>

ثم أضفت أDOMContentLoaded مستمع الحدث، للتأكد من تشغيل الشفرة عندما كان DOM جاهزًا:

<script>
document.addEventListener('DOMContentLoaded', event => {

}) </script>

هناك ، أضفت تكوين Firebase:

const firebaseConfig = {
  apiKey: "MY-API-KEY",
  authDomain: "MY-AUTH-DOMAIN",
  projectId: "MY-PROJECT-ID"
}

مررت هذا الكائن إلىfirebase.initializeApp()، ثم اتصلتfirebase.firestore()للحصول على المرجع إلى كائن قاعدة البيانات:

firebase.initializeApp(firebaseConfig)
const db = firebase.firestore()

الآن ، قمت بإنشاء برنامج نصي لملء معرفات المستخدم من قائمة كانت لدي في الخلفية ، باستخدام حلقة بسيطة:

const list = [/*...my list...*/]

list.forEach(item => { db.collection(‘users’).doc(item).set({}) })

.. وقمت بتشغيله مرة واحدة لملء قاعدة البيانات أنا أساسًا برمجيًاإنشاء مستند لكل مستخدم.

هذا مهم للغاية ، لأنني بمجرد أن أنشأت مستندًا ، فهذا يعني أنه يمكنني تقييد الأذونات لتحديث تلك المستندات فقط ، وعدم السماح بإضافة مستندات جديدة أو حذفها (وهو أمر سنفعله لاحقًا)

حسنًا ، لدي الآن بعض المنطق المعقد لتحديد معرف المستخدم ومعرف الدورة التدريبية ، والذي لن أخوض فيه لأنه لا يتعلق بمهمتنا هنا.

بمجرد أن جمعت ذلك ، يمكنني الحصول على إشارة إلى الكائن:

const id = /* the user ID */
const course = /* the course ID */
const docRef = db.doc(`membership/${id}`)

رائعة! يمكنني الآن الحصول على مرجع المستند من Firebase:

docRef.get().then(function(doc) {
  if (doc.exists) {
    const data = doc.data()
    document.querySelector('button')
      .addEventListener('click', () => {
      data[course] = true
      docRef.update(data)
    })
  } else {
    //user does not exist..
  }
})

كان منطقتي في الواقع أكثر تعقيدًا لأن لدي أجزاء متحركة أخرى ، لكنك حصلت على الفكرة!

أقوم بتهيئة بيانات المستند عن طريق الاتصالdoc.data()وعندما يتم النقر على الزر ، والذي أفترض أنه الزر لقول "لقد أكملت الدورة التدريبية" ، قمنا بربطtrueقيمة منطقية لمعرف النادي.

لاحقًا ، في عمليات التحميل اللاحقة لصفحة قائمة الدورات التدريبية ، يمكنني تهيئة الصفحة وتعيين فصل دراسي إذا اكتملت الدورة التدريبية ، مثل هذا:

for (const [key, value] of Object.entries(data[course])) {
  const element = document.querySelector('.course-' + course)
  if (element) {
    element.classList.add('completed')
  }
}

مشكلة الأذونات

لقد بدأت Firebase في وضع الاختبار ، هل تتذكر؟ مما يجعل قاعدة البيانات مفتوحة للجميع - كل شخص لديه مفاتيح الوصول ، والتي تكون عامة ويتم نشرها في الكود المشحون إلى الواجهة الأمامية.

لذلك كان علي أن أفعل شيئًا واحدًا: تحديد مستوى الإذن المسموح به.

وقد عثرت على مشكلة مهمة جدًا.

استخدام Firebase Console ، أسفلقواعد، يمكننا قطع الإذن. في البداية كانت هذه هي القاعدة الافتراضية:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

لقد غيرت القواعد إلىread, update، لذلك يمكن للمرء تحديث مستند فقط ، وليس إنشاء مستندات جديدة:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, update;
    }
  }
}

لكنني لم أتمكن من منع الأشخاص من استخدام Firebase API ، المتاح الآن مجانًا في المتصفح ، للتجول وإدراج جميع المستندات الأخرى في المجموعة - للوصول إلى ملفات الأشخاص الآخرين.

في حين أن هذا لم يتعامل مع أي بيانات حساسة ، لم يكن من الممكن شحن هذا الرمز.

نقل الكود من الواجهة الأمامية إلى الخلفية عبر واجهة برمجة تطبيقات مخصصة

كانت قضية الإذن معطلة طريق.

فكرت في إزالة جميع الأكواد التي أمتلكها ، لكنني اكتشفت في النهاية أنه يمكنني إخفاء وصول واجهة برمجة التطبيقات بالكامل من المتصفح تمامًا واستخدام خدمة Node.js لتشغيل Firebase API.

هذه أيضًا طريقة شائعة لإخفاء المفاتيح الخاصة / السرية التي تتطلبها الخدمات: إخفائها خلف خادم تتحكم فيه.

بدلاً من استدعاء Firebase من المتصفح ، قمت بإنشاء مجموعة من نقاط النهاية على خادمي الخاص ، على سبيل المثال:

  • إضافة إلى/courseلتعيين الدورة على أنها مكتملة
  • إضافة إلى/dataللحصول على البيانات المرتبطة بالمستخدم

وأنا أصل إليهم باستخدامجلب API:

const options = {
    method: 'POST',
    headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
    },
    body: JSON.stringify({ id, course, lesson })
}
const url = BASE_URL + '/lesson'
fetch(url, options).catch(err => {
    console.error('Request failed', err)
})

يبقى كل منطق النقرات على الأزرار وما إلى ذلك في رمز جانب العميل ، بالطبع ، لقد نقلت منطق Firebase بعيدًا.

على جانب خادم Node.js ، قمت بتثبيت المسؤولfirebaseحزمة باستخدامnpm install firebaseوطلبت ذلك:

const firebase = require('firebase')

أقوم بإعداد ملفالتعبيرخادم للاستخدامكورسوقمت بتهيئة Firebase:

const firebaseConfig = {
  apiKey: process.env.APIKEY,
  authDomain: process.env.AUTHDOMAIN,
  projectId: process.env.PROJECTID
}

firebase.initializeApp(firebaseConfig) const db = firebase.firestore()

ثم يكون الرمز تمامًا مثل الرمز الذي استخدمته في الواجهة الأمامية ، إلا أنه يتم تنشيطه الآن على مكالمات نقطة نهاية HTTP. هذا هو الرمز الذي يُرجع مستندًا معينًا من مجموعتنا

const getData = async (id) =>  {
  const doc = await db.doc(`membership/${id}`).get()
  const data = doc.data()
  if (!data) {
    console.error('member does not exist')
    return
  }
  return data
}

app.post(’/data’, cors(), async (req, res) => { const id = req.body.id if (id) { res.json(await getData(id)) return } res.end() })

وإليك API لتعيين الدورة التدريبية على أنها مكتملة:

const setCourseAsCompleted = async (id, course) => {
  const doc = await db.doc(`membership/${id}`).get()
  const data = doc.data()
  if (!data) {
    console.error('member does not exist')
    return
  }
  if (!data[course]) {
      data[course] = {}
  }
  data[course]['done'] = true
  db.doc(`membership/${id}`).update(data)
}

app.post(’/course’, cors(), (req, res) => { const id = req.body.id const course = req.body.course if (id && course) { setCourseAsCompleted(id, course) res.end(‘ok’) return } res.end() })

هذا هو الأساس. هناك المزيد من التعليمات البرمجية المطلوبة للتعامل مع منطق آخر ، ولكن جوهر Firebase هو هذا الذي قمت بنشره. الآن يمكنني أيضًا إضافة مستخدم للخدمة من جانب الخادم ، وتقييد الوصول الآخر إلى Firebase API وتعزيز الأمان عليها.


المزيد من دروس الخدمات: