Organizowanie wielu funkcji

W miarę integrowania Cloud Functions z projektem kod może się rozrastać i zawierać wiele niezależnych funkcji. Możesz mieć zbyt wiele funkcji, aby zmieścić je w jednym pliku, lub różne zespoły mogą wdrażać różne grupy funkcji, co stwarza ryzyko, że jeden zespół zastąpi lub przypadkowo usunie funkcje innego zespołu. Cloud Functions oferuje różne sposoby organizowania kodu, aby ułatwić poruszanie się po funkcjach i ich utrzymanie.

Porządkowanie funkcji w bazach kodu

Możesz użyć właściwości codebase obiektu konfiguracji funkcji w firebase.json, aby zarządzać dużą kolekcją funkcji w wielu repozytoriach lub podpakietach w ramach konfiguracji monorepozytorium pojedynczego repozytorium:

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

Właściwość codebase jest obsługiwana w interfejsie wiersza poleceń Firebase w wersji 10.7.1 lub nowszej.

Zarządzanie wieloma repozytoriami

Właściwość codebase może uprościć zarządzanie wieloma repozytoriami. Przeanalizujmy przypadek, w którym masz 2 różne repozytoria wdrażające funkcje w tym samym projekcie w Firebase:

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

Bez adnotacji bazy kodu wiersz poleceń Firebase poprosiłby Cię o usunięcie funkcji zdefiniowanych w innym repozytorium w momencie wdrażania:

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

Możesz uniknąć tego problemu, dodając unikalną adnotację bazy kodu w sekcji konfiguracji funkcji w pliku firebase.json w każdym repozytorium projektu:

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

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

Dzięki adnotacji bazy kodu wiersz poleceń Firebase nie prosi już o usunięcie funkcji zdefiniowanych poza bezpośrednim repozytorium:

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

Zarządzanie wieloma pakietami źródłowymi (monorepozytorium)

Właściwość codebase może uprościć zarządzanie wieloma pakietami źródłowymi w jednym repozytorium. Przyjrzyjmy się przypadkowi, w którym masz katalog projektu Firebase z definicjami funkcji rozproszonymi w kilku podpakietach:

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

Ta konfiguracja pasuje do tych przypadków użycia:

  • Masz konfigurację monorepozytorium i różne zespoły zarządzają własnymi definicjami funkcji w odizolowanym pakiecie.
  • Masz funkcję z dużą zależnością zewnętrzną i długą inicjalizacją i chcesz odizolować ją od innych funkcji, które są wrażliwe na opóźnienia.

Aby obsługiwać taką konfigurację monrepo, zdefiniuj wiele konfiguracji funkcji w pliku firebase.json:

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

W tej konfiguracji interfejs wiersza poleceń Firebase wdraża funkcje ze wszystkich pakietów za pomocą jednego polecenia wdrażania:

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

Możesz też wdrożyć konkretną bazę kodu:

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

Zapisywanie funkcji w wielu plikach

Na początku pracy z Cloud Functions możesz umieścić kilka pierwszych funkcji w jednym pliku:

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

Zarządzanie nimi może być trudne, jeśli masz więcej niż kilka funkcji. Zamiast tego możesz umieścić całą logikę każdej funkcji w osobnym pliku i użyć pliku źródłowego jako listy eksportów:

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 *

Ta konfiguracja zakłada, że struktura katalogów projektu wygląda tak:

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

fn_impl: może mieć dowolną nazwę.

__init__.py: wymagany, ale może być pusty

Funkcje grupowe

W wielu projektach funkcje można podzielić na grupy logiczne, które powinny być wdrażane i utrzymywane razem. Możesz na przykład mieć grupę funkcji używanych do raportowania danych:

metrics.js


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

Możesz umieścić te funkcje w grupie, eksportując je w pliku index.js:

index.js


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

Po wdrożeniu funkcje będą miały prefiks z nazwą grupy, więc w tym przykładzie funkcje będą miały nazwy metrics-usageStats i metrics-nightlyReport.

Podczas wdrażania funkcji możesz ograniczyć działanie do jednej grupy:


firebase deploy --only functions:metrics

Dalsze kroki

Więcej informacji o Cloud Functions znajdziesz tutaj: