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

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

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

Еще одна особенность облачных функций заключается в том, что они позволяют подключаться к другим продуктам Firebase. С практической точки зрения это означает, что функции могут быть определены для запуска кода, похожего на побочный эффект, в результате чего-то (события), происходящего в другом продукте. Например, пользователь впервые проходит аутентификацию с использованием аутентификации Firebase в приложении, и необходимо отправить письмо новому пользователю на основе этого события; или новый документ только что был создан в коллекции в базе данных Firebase (или Cloud Firestore), и необходимо скопировать новые данные в электронную таблицу Google.

Типы облачных функций

Последний абзац во введении выше дает подсказку об одном из типов облачных функций (в зависимости от того, как они запускаются) — функциях, которые запускаются на основе события из другого продукта Firebase. Они называются фоновыми функциями. Другой тип — это функции, которые запускаются напрямую с помощью HTTP-запросов.

Различие между этими двумя типами тонкое, но оно также определяет, как развивается функция (определение функции). Основное внимание здесь будет сосредоточено на первом типе и пошаговом руководстве по решению примера проблемы, изложенной в предыдущем разделе — копировании записи нового документа в коллекцию Firestore в электронную таблицу Google.

Предпосылки

Функции выполняются в среде выполнения NodeJS. Две поддерживаемые версии NodeJS — 6 и 8 (бета). Это означает, что Javascript является поддерживаемым языком для определения функций (Typescript также поддерживается) с поддерживаемой версией языка, которая может работать в этих версиях Node. (Функции могут быть написаны на любой другой более высокой версии Javascript или ECMAScript или фактически на любом другом языке, если их можно транспилировать или скомпилировать в то, что может работать в этих версиях Node)

Необходимыми условиями для локальной разработки функций являются NodeJS (будет работать любая версия, более поздняя, ​​чем наименее поддерживаемая), NPM и библиотека firebase-tools.

Установка библиотеки Firebase-tools

Если есть уже существующий проект NPM, проще всего просто установить firebase-tools в качестве зависимости от разработчиков и сразу начать его использовать:

# Install
npm i -D firebase-tools
# Issue commands
npx firebase <command>

Кроме того, его можно установить глобально с помощью:

# Install
npm i -g firebase-tools
# Issue commands
firebase <command>
# but this is 2019, who still installs npm libs globally

или команды могут быть выданы напрямую, запустив

npx firebase-tools <command> # but this can relatively slow down developement

Инициализация облачных функций

Шаги аутентификации и инициализации необходимы для начала разработки облачных функций.

# sign in
npx firebase login

Это покажет процесс CLI, который предложит войти в систему через браузер и отобразит сообщение об успешном входе в систему.

# initialize cloud functions
npx firebase init functions

Будет показан процесс CLI для инициализации облачных функций для существующего проекта Firebase. После выполнения подсказок создаются следующие файлы и папки:

.firebaserc: базовый файл конфигурации для проекта Firebase. В зависимости от параметров, выбранных в процессе инициализации, может быть включен объект project, имеющий свойство default, равное имени выбранного проекта.

функции: каталог, в котором определены функции. По сути, это каталог NPM, так как он включает в себя файл package.json и файл index.js, который является точкой входа для функций.

Определение облачных функций

Функция определяется простым написанием функции Javascript и ее экспортом в файл index.js, указанный выше. Функция может быть определена в другом файле, но должна быть экспортирована в файл ввода.

Функция, активируемая HTTP, должна быть определена как обратный вызов с определенной подписью в ответ на HTTP-запрос. Обратному вызову передаются параметры, которые используются для обработки цикла запрос-ответ.

// index.js
exports.<functionName> = (req, res) => { //similar to express
   // body of function
}

Фоновые функции определяются совсем по-другому. Пакет firebase-functions NPM — это сопутствующая библиотека, которая автоматически устанавливается в описанном выше процессе инициализации. Он включает функции, которые принимают обратные вызовы для обработки событий, происходящих в других продуктах. Аргументы, передаваемые обратному вызову, определяются продуктом и событием.

// index.js
const functions = require('firebase-functions');
// rough definition
exports.<cloudFunction> = functions.<product>.<event>(callback)

Чтобы обработать конкретный случай в указанном фокусе:

// index.js
const functions = require('firebase-functions');
// path sections sorrounded with curly braces are refferred to as wildcards - more on this below
exports.copyDocumentToSheet = functions.firestore.document('/<collection>/{documentId}').onCreate(callback)
// callback
// a snapshot of the new document is passed to the callbackconst const callback = (docSnapshot) => {
    // do stuffs with the document
}

Примечание о подстановочных знаках в функциях Firestore

Хотя на документы можно напрямую ссылаться, указав известный идентификатор документа в пути к методу document объекта function firestore , это, скорее всего, не будет иметь место при разработке функций Firestore. В большинстве случаев интерес представляет собой событие из любого документа в коллекции. Вот для чего нужны подстановочные знаки. Они позволяют указать, что функция должна запускаться для указанного события в любом документе в коллекции.

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

const callback = (docSnapshot, context) => {
   // get document identifier represented by wildcard
   const id = context.params.documentId
   // ...
}

Определение и установка зависимостей для облачных функций

Зависимости определяются и устанавливаются так же, как это делается в любом проекте NPM — путем установки зависимости с помощью npm i <option> <dependency>, которая автоматически перечисляет зависимость в package.json, созданном в каталоге функций.

Указание переменных среды (или переменных конфигурации) для функций

Переменные конфигурации задаются выполнением определенных команд из утилитыfirebase-tools. Это единственный случай, когда кажется, что есть тонкая разница в разработке облачных функций и разработке обычного приложения NodeJS.

В приведенной ниже команде показано, как установить переменную конфигурации, к которой можно получить доступ в functions.

npx firebase functions:config:set <configName>=<value>
# or to set related configs under a single object
npx firebase functions:config:set <configName>.<key>=<value>

Команда ниже показывает, как просмотреть установленные переменные конфигурации:

npx firebase functions:config:get
// shows the config vars as JSON in the terminal
{
  "configName": "value"
}
or for nested config vars
{
  "configName": {
    "key": "value"
  }
}

Чтобы получить доступ к переменным конфигурации в функциях:

const functions = require('firebase-functions');
// configVars will be a parsed object of the JSON shown when the config:get command is issued
const configVars = functions.config()

Локальный запуск облачных функций

Утилита firebase-tools предоставляет команду, позволяющую запустить эмулятор, из которого определенные функции могут запускаться локально.

npx firebase functions:shell

Это запустит эмулятор с определенными функциями, скопированными в оболочку и доступными по имени функции.

Например, чтобы вызвать функцию Firestore, связанную с событием onCreate документа:

// newDocument should be a regular Javascript object
firebase > functionName(newDocument)

newDocument в теле функции будет снимком документа, а вызов .data() на снимке вернет объект, переданный функции.

Локальный доступ к переменным конфигурации в определенных функциях

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

npx firebase functions:config:get > functions/.runtimeconfig.json

Это создаст файл .runtimeconfig.json в каталоге функций с ранее установленными переменными конфигурации, скопированными в файл. Выполнение этой команды требуется только для локального доступа к переменным конфигурации. Без него вызов functions.config() в определении функции вернет пустой объект.

Применение всего на практике

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