Как упорядочивать несколько функций (первое поколение)

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

Организация функций в базах кода

Вы можете использовать свойство codebase объекта конфигурации функций в firebase.json, чтобы управлять большим набором функций в нескольких репозиториях или подпакетах в рамках одной настройки монорепозитория:

# firebase.json
"functions": {
  "codebase": "my-codebase"
  # NOTE: Codebase must be less than 63 characters and can contain only
  # lowercase letters, numeric characters, underscores, and dashes.
}

Свойство codebase поддерживается в интерфейсе командной строки Firebase версии 10.7.1 и выше.

Управление несколькими хранилищами

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

$  tree .
├── repoA
│   ├── firebase.json
│   └── functions
│       ├── index.js
│       └── package.json
└── repoB
    ├── firebase.json
    └── functions
        ├── index.js
        └── package.json

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

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
✔  functions: functions folder uploaded successfully
The following functions are found in your project but do not exist in your local source code:
        fn1FromRepoB
        fn2FromRepoB
        ...
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N)

Чтобы избежать этой проблемы, добавьте уникальную аннотацию базы кода в раздел конфигурации функций файла firebase.json в каждом репозитории проекта:

# repoA/firebase.json
"functions": {
  "codebase": "repo-a"
}

# repoB/firebase.json
"functions": {
  "codebase": "repo-b"
}

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

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
✔  functions: functions folder uploaded successfully
#  Gleefully ignores functions from repoB
i  functions: creating Node.js 16 function fnFromRepoA (us-central1)...
✔  Deploy Complete!

Как управлять несколькими пакетами исходного кода (монорепозиторий)

Свойство codebase упрощает управление несколькими исходными пакетами в одном хранилище. Рассмотрим случай, когда у вас есть каталог проекта Firebase с определениями функций, распределенными по нескольким подпакетам:

$  tree .
├── firebase.json
├── teamA
│   ├── index.js
│   └── package.json
└── teamB
    ├── index.js
    └── package.json

Такая настройка подходит для следующих случаев:

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

Чтобы поддерживать такую настройку monrepo, определите несколько конфигураций функций в firebase.json:

"functions": [
  {
    "source": "teamA",
    "codebase": "team-a"
  },
  {
    "source": "teamB",
    "codebase": "team-b"
  },
]

При такой конфигурации интерфейс командной строки Firebase развертывает функции из всех пакетов с помощью одной команды:

$ firebase deploy --only functions
i  deploying functions
i  functions: preparing codebase team-a for deployment
i  functions: preparing codebase team-b for deployment
i  functions: creating Node.js 16 function team-a:helloATeam(us-central1)...
i  functions: creating Node.js 16 function team-b:helloBTeam(us-central1)...
...

Вы также можете развернуть определенную базу кода:

$ firebase deploy --only functions:team-b
i  deploying functions
i  functions: preparing codebase team-b for deployment
i  functions: updating Node.js 16 function team-b:helloBTeam(us-central1)...
...

Как написать функции в нескольких файлах

Начиная работу с Cloud Functions, вы можете поместить первые несколько функций в один файл:

index.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

main.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello bar!")

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

Node.js

foo.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});

bar.js

const functions = require('firebase-functions/v1');
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

index.js

const foo = require('./foo');
const bar = require('./bar');
exports.foo = foo.foo;
exports.bar = bar.bar;

Python

foo.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

bar.py

from firebase_functions import https_fn

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

main.py

from fn_impl.foo import *
from fn_impl.bar import *

В этом примере используется следующая структура каталогов проекта:

my-project
├── firebase.json
└── functions
    ├── fn_impl
    │   ├── __init__.py
    │   ├── foo.py
    │   └── bar.py
    ├── main.py
    └── requirements.txt

fn_impl: может иметь любое название.

__init__.py: обязательный, но может быть пустым.

Функции для работы с группами

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

metrics.js


const functions = require('firebase-functions/v1');
exports.usageStats = functions.https.onRequest((request, response) => {
  // ...
});
exports.nightlyReport = functions.https.onRequest((request, response) => {
  // ...
});

При экспорте в файл index.js эти функции можно объединить в группу:

index.js


// Export both functions from metrics.js in the "metrics" group:
//  - metrics-usageStats
//  - metrics-nightlyReport
exports.metrics = require('./metrics');

При развертывании к функциям будет добавлен префикс с названием их группы, поэтому в этом примере функции будут называться metrics-usageStats и metrics-nightlyReport.

При развертывании функций вы можете ограничить действие одной группой:


firebase deploy --only functions:metrics

Дальнейшие действия

Чтобы узнать больше о Cloud Functions, ознакомьтесь со следующими статьями: