Недавно я создал API, реализующий базовые функции создания, чтения, обновления, удаления (CRUD) для приложения CV, которое я собираюсь создать.

Сначала я построил потребителя API, используя Pact, и как только я был счастлив, что мой API выполнил условия контракта с потребителем с помощью заглушек, я перешел на реализацию Firebase для хранения и аутентификации, используя контракт с потребителем, чтобы убедиться, что я ничего не сломал.

Это был мой первый опыт использования Firebase, поскольку я обычно отключаюсь в свою зону комфорта Django и PostgreSQL, если мне нужно реализовать авторизацию, но я использовал Restify в прошлом и получил от этого удовольствие, поэтому решил попробовать Firebase, как и обещал. легко реализовать аутентификацию и хранение.

Добавление аутентификации с помощью Firebase

Firebase из коробки поддерживает довольно внушительное количество бэкэндов аутентификации, и я думаю, вам будет сложно найти пользователя, у которого нет хотя бы одной учетной записи с предлагаемыми бэкэндами, на момент написания они включают:

  • Адрес электронной почты и пароль
  • Электронный адрес и одноразовая ссылка
  • Телефонный номер
  • Facebook
  • Твиттер
  • Google
  • Microsoft
  • яблоко

В следующих примерах я использую адрес электронной почты и пароль.

Создание нового пользователя

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

Для этого вы можете использовать функцию firebase.auth().createUserWithEmailAndPassword(), которая создает учетную запись или выдает ошибку, а затем использовать функцию firebase.auth().currentUser.getIdToken() для получения токена аутентификации для этого пользователя.

Войти существующего пользователя

Как только пользователь вошел в ваше приложение в первый раз, он может войти в систему с помощью функции firebase.auth().signInWithEmailAndPassword(), которая, как и функция createUserWithEmailAndPassword(), выполняет вход в систему, но требует вызова getIdToken() для получения их токена аутентификации.

Получение пользователя из JWT

После того, как пользователь зарегистрировался или успешно вошел в систему и получил свой токен аутентификации, ему нужно будет использовать токен для аутентификации при последующих вызовах API, это часто делается с использованием его в качестве токена-носителя в Заголовок авторизации (например, Authorization: Bearer [TOKEN]).

В Restify вы можете использовать функции в качестве промежуточного программного обеспечения, которое будет применяться ко всем вызовам сервера и выполнять задачи до вызова контроллера.

Я создал для этого функцию, а затем применил ее с помощью функции server.use(), функции промежуточного программного обеспечения принимают те же (req, res, next) аргументы, что и обычные контроллеры.

Чтобы получить запись пользователя от JWT, вам необходимо вызвать admin.auth().verifyIdToken(), который вернет информацию, содержащуюся в токене. Затем вы можете использовать свойство sub с admin.auth().getUser() для загрузки записи пользователя.

Как только я получил пользовательскую запись, я использовал функцию Restify req.set() для установки объекта пользователя в запросе, затем это можно было использовать через req.get() в последующих контроллерах для доступа к пользовательской записи.

GOTCHA - SignOut не отзывает токен

Документация Firebase предполагает, что для завершения сеанса пользователя вы вызываете firebase.auth().signOut(), но на самом деле это не отменяет токен авторизации пользователя, а это означает, что пользователь может получить доступ к документам в течение часа после того, как токен, который он использует, был создан.

Вместо этого signOut() сообщает Firebase не обновлять токен пользователя, если срок его действия истекает. Я еще не нашел способа полностью отменить доступ при выходе из системы, так что об этом следует помнить при использовании Firebase для аутентификации.

Использование Firebase в качестве хранилища

Firebase использует структуру хранилища документов. Подобно большинству решений NoSQL, он не привязан к схеме и, как таковой, может использоваться для хранения различных данных между документами.

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

Firebase использует collections как средство разделения типов документов, поэтому, если вы хотите работать с серией документов, содержащих данные, например, о собаках, вы должны использовать admin.firestore().collection('dogs'), а затем выполнять операции с коллекцией.

Внутри коллекции есть ряд ссылок на документы; эти справочные объекты определяют то, как вы взаимодействуете с созданием, удалением, обновлением и чтением документа.

Настройка вещей

Для работы с хранилищем Firebase вам необходимо инициализировать Firestore с помощью admin.firestore(), который возвращает объект, который вы можете использовать для выполнения операций с документом.

Создание документа

Создать документ действительно просто, вы просто создаете новый документ в коллекции, используя db.collection('dogs').doc(), который вернет ссылку на вновь созданный документ.

В этой ссылке на документ есть метод set(), который используется для обновления данных, хранящихся в этом документе.

Поиск документа

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

Хотя это достаточно легко сделать, если у вас все еще где-то хранится ссылка, часто бывает, что вам нужно выполнить поиск, чтобы найти ее, или выполнить поиск в нескольких документах на основе условия (например, идентификатор пользователя, чтобы получить все документы для этого пользователя).

Для поиска документа вам необходимо получить ссылку на коллекцию, в которой хранится документ, в этой ссылке есть метод where(), который можно использовать для предоставления предиката для поиска.

После выполнения поиска с использованием функции get(), предоставленной where(), полученный объект будет иметь свойство docs, которое представляет собой массив возвращенных документов.

В следующем примере я выполняю поиск всех документов, связанных с пользователем, а затем возвращаю пользователю массив идентификаторов.

Следует отметить, что Firebase - это база данных NoSQL. Вам нужно будет найти в своей схеме средства для хранения свойств, по которым вы хотите выполнять поиск, например userId, поскольку они не будут предоставляться по умолчанию, в моем приложении мне пришлось расширить схему моего метаобъект документа, чтобы включить userId для поиска по нему.

Чтение документа

Прямой доступ к документу через идентификатор документа для чтения из него данных осуществляется аналогично поиску, с той лишь разницей, что предоставление коллекции с идентификатором документа означает, что нет необходимости предоставлять предикат.

Вы можете получить ссылку на документ, используя db.collection('dogs').doc(DOCUMENT_ID), который возвращает ссылку на документ в коллекции, аналогично поиску, который затем нужно вызвать get(), чтобы получить ссылку на сам документ.

Получив ссылку на документ, вы можете использовать свойство ссылки exists, чтобы проверить, действительно ли документ существует, и вызвать метод data() для чтения самих данных документа.

Обновление документа

Обновления документа выполняются для ссылки на документ и предоставляются как один объект глубины.

Любые свойства вложенных объектов, которые необходимо обновить, должны быть представлены в виде точечного синтаксиса (например, для обновления свойства userId объекта meta необходимо указать meta.userId в качестве ключа в объекте обновления).

Когда я имел дело с необходимостью использовать точечный синтаксис для вложенных объектов, я нашел полезной библиотеку dotize.

Получив данные обновления в правильном формате, вы вызываете метод update() для ссылки на документ.

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

Удаление документа

Удалить документ очень просто, это просто вызов delete() для ссылки на документ.

Заглушка Firebase для тестирования

Как упоминалось в начале этого поста, Я использовал Pact для отработки TDD при создании своей реализации API, и из-за статической природы пактов, созданных Pact, мне нужно использовать заглушенные значения в моих тестах.

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

Я использую Jest в качестве средства запуска тестов, потому что у него есть невероятно полезная функция автоматического моделирования, что означает, что размещение модулей в каталоге __mock__ в корне проекта будет применять эти модули как заглушки для фактических модулей, импортированных в код. под тестом.

Это устраняет необходимость использовать библиотеки, такие как proxyquire и Rewire, для управления библиотеками, с которыми будет взаимодействовать тестируемый код.

Издевательство над firebase-admin

firebase-admin - это модуль, обеспечивающий чтение JWT и функции хранилища данных, поэтому именно в нем выполняется большинство вызовов firebase.

Следующий модуль должен быть доступен по адресу __mocks__/firebase-admin.js, чтобы он работал, вы можете использовать mockImplementation для любого из jest.fn() вызовов, чтобы реализовать поведение, которое вы хотите протестировать.

Издевательская база огня

firebase/app - это модуль, который предоставляет функциональные возможности для создания новых учетных записей пользователей, а также для их входа и выхода, а также для получения текущего пользователя. Т

В таком случае это относительно легкий макет.

Следующий модуль должен быть доступен по адресу __mocks__/firebase/app.js, чтобы он работал, аналогично приведенному выше вы можете использовать mockImplementation для реализации поведения, которое хотите протестировать.

Резюме

Firebase определенно выполняет обещание простой реализации аутентификации и хранения, и с ней приятно работать, особенно в сочетании с модульным подходом с использованием промежуточного программного обеспечения в Restify и функции автоматического моделирования Jest во время тестирования.

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

Примечание команды Plain English

Вы знали, что у нас четыре публикации? Проявите немного любви, предложив им следующие слова: JavaScript на простом английском, AI на простом английском, UX на простом английском , Python на простом английском - спасибо и продолжайте учиться! Мы также запустили YouTube и хотели бы, чтобы вы поддержали нас, подписавшись на наш канал Plain English.

И, как всегда, Plain English помогает продвигать хороший контент. Если у вас есть статья, которую вы хотели бы отправить в какую-либо из наших публикаций, отправьте электронное письмо на адрес [email protected] с вашим именем пользователя Medium и тем, о чем вы хотите написать, и мы вернуться к вам!