Как использовать Firebase Firestore в качестве базы данных

Учебное пособие по настройке Firestore в качестве базы данных - очень удобное решение ваших проблем с хранилищем!

Мне нужно было создать хранилище для некоторых данных для моегочленство в клубе, место, где я преподаю программирование.

Я хотел, чтобы мои пользователи могли вручную сказать «Я прошел этот курс», нажав кнопку.

По сути, я хотел сохранить определенный объект для каждого пользователя.

Настройка Firebase

Я решил использоватьFirebaseдля этого, и в частностиБаза данных Firestoreони предоставляют.

Уровень бесплатного пользования для него велик: до 1 ГБ хранимых данных и 10 ГБ передачи по сети в месяц. Сильно превышаю мои оценки того, что мне нужно!

Откройте веб-сайт Firebase по адресуhttps://firebase.google.com/

Firebase - это продукт Google, поэтому после входа в Google вы, по сути, также входите в Firebase.

Я создал новый проект Firebase, нажав «Создать проект».

Я дал ему имя:

Вот и все:

Я щелкнул значок «Интернет» рядом с iOS и Android и ввел название приложения:

И Firebase сразу же предоставила мне необходимые ключи доступа вместе с некоторыми примерами кода:

Сразу после этого Firebase предложила мне добавить некоторые правила безопасности для базы данных.

По умолчанию вы можете выбрать 2 вещи: открытые для всех или закрытые для всех. Я начал открываться для всех, что они называюттестовый режим.

Вот и все! Я был готов к работе, создав коллекцию.

Что за коллекция? В терминологии Firestore мы можем создавать множество разных коллекций и назначать документы каждой коллекции.

Тогда документ может содержать поля и другие коллекции.

Это не сильно отличается от других баз данных NoSQL, напримерMongoDB.

Очень рекомендую посмотретьплейлист YouTube по этой теме, это очень хорошо сделано.

Итак, я добавил свою коллекцию, которую назвалusers.

Я хотел идентифицировать каждого пользователя с помощью специальной строки, которую я называюid.

Код внешнего интерфейса

Теперь мы переходим к части JavaScript.

В нижний колонтитул я включил эти 2 файла, предоставленные 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 в разделеПравила, мы можем урезать разрешение. Изначально это было правилом по умолчанию:

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, теперь свободно доступный в браузере, для игры и перечисления всех других документов в коллекции, получая доступ к файлам других людей.

Хотя при этом не обрабатывались конфиденциальные данные, отправить этот код было невозможно.

Перенос кода из внешнего интерфейса в серверную часть через настраиваемый API

Проблема с разрешением была преградой на пути.

Я думал об удалении всего кода, который у меня был, но в конце концов понял, что могу полностью скрыть весь доступ к 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')

Я создалвыражатьсервер для использованияCORSи я инициализировал 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 такова, что я опубликовал. Теперь я также могу добавить пользователя для своей серверной службы и ограничить весь другой доступ к API Firebase и усилить его безопасность.


Дополнительные руководства по услугам: