複数の関数を整理する


プロジェクトに Cloud Functions を統合すると、多くの独立した関数を収めるようにコードを拡張できます。ただし、1 つのファイルに収めるには関数が多すぎたり、さまざまなチームがさまざまな関数グループをデプロイすると、あるチームが別のチームの関数を上書きしたり、誤って削除したりするおそれがあります。関数の処理とメンテナンスが簡単になるように、Cloud Functions には、コードを整理するさまざまな方法が用意されています。

コードベースで関数を整理する

firebase.json にある関数の構成オブジェクトの codebase プロパティを使用すると、単一リポジトリの 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.
}

codebase プロパティは、Firebase CLI v10.7.1 以降でサポートされています。

複数のリポジトリの管理

codebase プロパティは、複数のリポジトリの管理を簡素化できます。同一の Firebase プロジェクトに関数をデプロイするリポジトリが 2 つあるケースについて考えます。

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

コードベースのアノテーションがないと、Firebase CLI では、デプロイの時点で、もう一方のリポジトリで定義されている関数を削除するよう求めるメッセージが表示されます。

$ (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 CLI で、即時リポジトリの外部で定義されている関数の削除を求めるメッセージは表示されなくなります。

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

複数のソース パッケージの管理(monorepo)

codebase プロパティを使用すると、単一リポジトリにある複数のソース パッケージの管理を簡素化できます。複数のサブパッケージに関数の定義が分散している Firebase プロジェクト ディレクトリがあるケースについて考えます。

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

この設定は、以下のユースケースに適しています。

  • monorepo を設定しており、さまざまなチームが独自の関数の定義を、隔絶されたパッケージで管理している。
  • 外部依存関係が高く、初期化の実行時間が長い関数があるため、レイテンシの影響を受けやすい他の関数からその関数を分離したい。

このような monrepo の設定を使用できるようにするには、firebase.json で複数の関数の構成を定義します。

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

この構成を使用すると、Firebase CLI の 1 つのデプロイ コマンドにより、すべてのパッケージから関数がデプロイされます。

$ 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 を初めて使用する場合は、最初のいくつかの関数を 1 つのファイルにまとめることができます。

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-usageStatsmetrics-nightlyReport)。

関数をデプロイするときに、アクションを 1 つのグループに限定できます。


firebase deploy --only functions:metrics

次のステップ

Cloud Functions の詳細については、以下をご覧ください。