Organizar várias funções


À medida que você integra o Cloud Functions ao projeto, seu código pode se expandir para conter muitas funções independentes. A quantidade de funções que você tem pode exceder o limite de um só arquivo ou talvez equipes diferentes implantem diversos grupos de funções e criem o risco de substituir ou excluir acidentalmente as funções de outras. O Cloud Functions oferece maneiras diferentes de organizar seu código para facilitar a navegação e manutenção das funções.

Organizar funções em bases de código

É possível usar a propriedade codebase do objeto de configuração de funções em firebase.json para gerenciar uma grande coleção de funções em vários repositórios ou subpacotes em uma configuração única de repositório monorepo:

# 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.
}

A propriedade codebase é compatível com a CLI do Firebase v10.7.1 e posteriores.

Como gerenciar vários repositórios

A propriedade codebase ajuda a simplificar o gerenciamento de vários repositórios. Vamos examinar um caso em que há dois repositórios diferentes que implantam funções no mesmo projeto do Firebase:

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

Sem as anotações da base de código, a CLI do Firebase solicitava que você excluísse funções definidas no outro repositório no momento da implantação:

$ (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)

Para evitar esse problema, adicione uma anotação da base de código exclusiva na seção de configuração de funções do firebase.json em cada repositório do projeto:

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

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

Com a anotação da base de código, a CLI do Firebase não solicita mais a exclusão de funções definidas fora do repositório imediato:

$ (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!

Como gerenciar vários pacotes de origem (monorepo)

A propriedade codebase simplifica o gerenciamento de vários pacotes de origem em um único repositório. Vamos examinar um caso em que você tem um diretório de projeto do Firebase com definições de funções distribuídas por vários subpacotes:

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

Essa configuração é adequada para os seguintes casos de uso:

  • Você tem uma configuração do monorepo e diferentes equipes gerenciam as próprias definições de função em um pacote isolado.
  • Você tem uma função com uma dependência externa pesada e uma inicialização de longa duração e quer isolar essa função de outras que são sensíveis à latência.

Para oferecer compatibilidade com a configuração do monorepo, defina várias configurações de funções em firebase.json:

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

Com essa configuração, a CLI do Firebase implanta funções de todos os pacotes em um comando de implantação único:

$ 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)...
...

Também é possível implantar uma base de código específica:

$ 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)...
...

Gravar funções em vários arquivos

Ao começar a usar o Cloud Functions, é possível que você coloque suas primeiras funções em um único arquivo:

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!")

Isso pode ser difícil de gerenciar caso haja um grande número de funções. Em vez disso, é possível colocar toda a lógica de cada função no próprio arquivo e usar o arquivo de origem como uma lista de exportações:

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 *

Essa configuração pressupõe uma estrutura de diretórios do projeto semelhante à seguinte:

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

fn_impl: pode ter qualquer nome

__init__.py: obrigatório, mas pode estar vazio

Funções de grupo

Em muitos projetos, as funções podem ser separadas em grupos lógicos que precisam ser implantados e mantidos juntos. Por exemplo, é possível ter um grupo de funções usadas para gerar relatórios de métricas:

metrics.js


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

É possível colocar essas funções em um grupo ao exportá-las no arquivo index.js:

index.js


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

Quando implantadas, as funções são prefixadas com o nome do grupo. Portanto, neste exemplo, as funções seriam chamadas de metrics-usageStats e metrics-nightlyReport.

Ao implantar funções, é possível limitar a ação a um único grupo:


firebase deploy --only functions:metrics

Próximas etapas

Para saber mais sobre o Cloud Functions, consulte: