1つのFirebaseプロジェクト内で複数のミニアプリを管理する
Firebaseのミニアプリをぽんぽんと作ろうと思うと,とても便利なのだけど,ミニアプリごとにFirebaseのプロジェクトを立てているとすぐに上限に達してしまう[1]ので,1つのプロジェクトの中にミニアプリを詰め込みたくなりますよね.
特に,自分はCloudFunctionsをよく使うのですが,関数を複数作って管理するのは良いのですが,複数のミニアプリの関数を全て1つのpackage.jsonで管理していくのはしんどいと思います.
Hostingについては比較的複数のWebサイトを別々に管理するのが簡単ですが,CloudFunctionsについて,複数のミニアプリで整理して管理する方法について調べてみました.
新しいプロジェクトを作成する
新しいプロジェクトを作成しながら試していきます.
$ mkdir test-multi-mini-app
$ cd test-multi-mini-app
$ firebase init
...
...
# (cloud functionsだけ,TypeScriptの設定で初期化しました)
...
...
$ tree
.
├── firebase.json
└── functions
├── node_modules
├── package-lock.json
├── package.json
├── src
│ └── index.ts
├── tsconfig.dev.json
└── tsconfig.json
初期化された時に生成されたindex.tsの中にhelloWorld
関数があると思うので,コメントアウトを外して以下のようなものをそのまま公開してみます.
import * as functions from "firebase-functions";
// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript
export const helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
$ firebase deploy --only functions
=== Deploying to '<プロジェクト名>'...
i deploying functions
...
...
...
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/my-kitchensink/overview
https://us-central1-<プロジェクト名>.cloudfunctions.net/helloWorld
にアクセスすればHello from Firebase!
と表示されるはずです
複数の関数を作る
では,関数を増やしてみましょう.
index.tsに関数を2つほど増やしてみます
import * as functions from "firebase-functions";
// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript
export const helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
export const helloWorld2 = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase2!!");
});
export const helloWorld3 = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase3!!!");
});
確認するまでもないですがこれで
- https://us-central1-<プロジェクト名>.cloudfunctions.net/helloWorld
- https://us-central1-<プロジェクト名>.cloudfunctions.net/helloWorld2
- https://us-central1-<プロジェクト名>.cloudfunctions.net/helloWorld3
という3つの関数が公開されました.
3つのミニアプリと言い換えてもいいかもしれません.
複数の関数をファイルごとに分ける
ですが,一つのindex.tsファイルの中に関数を増やしていくのは得策ではありません.
関数ごとにファイルを分割するのが良いですよね[2].
以下のようにファイルを変更しました
$ tree .
.
├── firebase.json
└── functions
├── lib
├── node_modules
├── package-lock.json
├── package.json
├── src
│ ├── hello.ts
│ ├── hello2.ts
│ ├── hello3.ts
│ └── index.ts
├── tsconfig.dev.json
└── tsconfig.json
const myFunctions = {
hello: "./hello",
hello2: "./hello2",
hello3: "./hello3",
};
const loadMyFunctions = (funcsObj: { [key: string]: string }): void => {
for (const name in funcsObj) {
if ((!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === name) && Object.hasOwnProperty.call(funcsObj, name)) {
exports[name] = require(funcsObj[name]);
}
}
};
loadMyFunctions(myFunctions);
import * as functions from "firebase-functions";
module.exports = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
import * as functions from "firebase-functions";
module.exports = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase2!!");
});
import * as functions from "firebase-functions";
module.exports = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase3!!!");
});
この内容でデプロイすれば,動作は全く同じですが,関数ごとにファイルを分けることができました.
ですが,あくまでindex.tsを分割したにすぎないので,全く関連のないミニアプリだとこの方法で管理するのはちょっと気持ち悪い感じです.
複数の関数を別のプロジェクトに分割する
そこで,functionsディレクトリにあるnode.jsと同じような設定で,複数のプロジェクトを作成します.
残念ながらfirebase init functions
のような便利なコマンドがないので,手作業で作成する必要がありそうです[3].
先ほどのディレクトリを以下のように変更しました.
$ tree .
.
├── firebase.json
└── functions
├── lib
├── node_modules
├── package-lock.json
├── package.json
├── src
│ ├── hello.ts
│ ├── hello2.ts
│ ├── hello3.ts
│ └── index.ts
├── tsconfig.dev.json
└── tsconfig.json
$ tree .
.
├── apps
│ ├── app-hello
│ │ ├── lib
│ │ ├── node_modules
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── hello.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.dev.json
│ │ └── tsconfig.json
│ ├── app-hello2
│ │ ├── lib
│ │ ├── node_modules
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── hello2.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.dev.json
│ │ └── tsconfig.json
│ └── app-hello3
│ ├── lib
│ ├── node_modules
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── hello3.ts
│ │ └── index.ts
│ ├── tsconfig.dev.json
│ └── tsconfig.json
└── firebase.json
それぞれのindex.tsはそれぞれのhello?.tsを読むだけに修正するだけです.
hello?.tsの中身は全く同じものです.
そして,ここが最重要ですが,firebase.jsonを修正します
{
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
}
{
"functions": [
{
"source": "apps/app-hello",
"codebase": "app-hello",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
},
{
"source": "apps/app-hello2",
"codebase": "app-hello2",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
},
{
"source": "apps/app-hello3",
"codebase": "app-hello3",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
]
}
source
の場所やcodebase
の名前は適宜読み替えてもらって良いですが,このように指定してあげることで,CloudFunctionsを同じFirebaseのプロジェクトの中で完全に別のnode.jsのアプリとして管理する(Monorepoっぽい感じで)ことができるようになりました.
初期化に時間がかかる処理を別プロジェクトとして分離したい場合などにも有効でしょう.Firebaseの公式ドキュメントにも少し記載があります.
デプロイするときには通常通りに
$ firebase deploy --only functions
で全てをまとめてデプロイすることもできますし,
$ firebase deploy --only function:app-hello2
のように,codebase
で指定したプロジェクトのみをデプロイすることもできます.
変更のあるプロジェクトをデプロイできるので手軽です.
仮に関数名が被っていた場合にはまとめてデプロイした場合にはエラーが出てくれますが,codebase
を指定した場合には,既存の関数を上書きするようにデプロイされてしまいますから注意が必要です.
これでミニアプリを大量に管理できるようになったと思います.
何か間違いやもっと良い方法があればコメントで教えてください.
参考
-
上限を引き上げる方法はありますが面倒ですよね.参考:Firebaseアカウントあたりのプロジェクト数 ↩︎
-
「Firebase, 関数, 複数」とかでググると関数ごとにファイルを分ける方法がたくさん出てくると思います.参考:Firebase複数のファイルに関数を書き込む ↩︎
-
自分は
firebase init functions
で生成されたfunctions
ディレクトリをapps/hoge/functions
とかに移動させるのが楽なのかな〜と思いました ↩︎
Discussion