【Nuxt3 × SSR】Firebase CLI 12系でSSR用関数を設定する方法

Firebase CLIの12系において、とサーバーサイドレンダリング(SSR)用関数の設定方法に変更がありました。今回はregionの指定、メモリ割り当て、実行タイムアウトの設定などを簡単に。

2024年01月31日
関連記事

前提

  • firebase-tools 12.x
  • nuxt3.x
  • node: “20” | “18” | “16”
  • gen: 1 #第一世代を使う

結論

とりあえず結論だけ欲しい方向けに、完成系を書いておきます。
設定はこの通り。

export default defineNuxtConfig({
  ~~~
  nitro: {
    preset: "firebase",
    firebase: {
      nodeVersion: '18',
      gen: 1, #第一世代
      region: 'asia-northeast1',
      runtimeOptions: {
        memory: '512MB'
      }
    },
  }
  ~~~
})

Nito

NitroはNuxt3で採用されているサーバーエンジンです。SSR用関数はこいつを使ってデプロイされているようです。

ただ、公式を見ても詳しく載っていないのでとっても悩みました。

仕方ないので実装を追ってみると、すぐ解決。

interface FirebaseOptionsBase {
    gen: 1 | 2;
    /**
     * Firebase functions node runtime version.
     * @see https://cloud.google.com/functions/docs/concepts/nodejs-runtime
     */
    nodeVersion?: "20" | "18" | "16";
    /**
     * When deploying multiple apps within the same Firebase project
     * you must give your server a unique name in order to avoid overwriting your functions.
     *
     * @default "server"
     */
    serverFunctionName?: string;
}
interface FirebaseOptionsGen1 extends FirebaseOptionsBase {
    gen: 1;
    /**
     * Firebase functions 1st generation region passed to `functions.region()`.
     */
    region?: Parameters<typeof region>[0];
    /**
     * Firebase functions 1st generation runtime options passed to `functions.runWith()`.
     */
    runtimeOptions?: RuntimeOptions;
}
interface FirebaseOptionsGen2 extends FirebaseOptionsBase {
    gen: 2;
    /**
     * Firebase functions 2nd generation https options passed to `onRequest`.
     * @see https://firebase.google.com/docs/reference/functions/2nd-gen/node/firebase-functions.https.httpsoptions
     */
    httpsOptions?: HttpsOptions;
}

今回は第一世代なので FirebaseOptionsGen1 ですね。
とってもシンプルになっていました。

メモリ割り当て

SSRとなると、デフォルト(256MB)ではパフォーマンスが悪すぎました。
最低でも 512MB ストレスなく使うには 1GB は欲しいところです。

肝心の設定は runtimeOptions から行う必要があります。
ソースは以下の通り。

export interface RuntimeOptions {
    /**
     * Failure policy of the function, with boolean `true` being equivalent to
     * providing an empty retry object.
     */
    failurePolicy?: FailurePolicy | boolean;
    /**
     * Amount of memory to allocate to the function.
     */
    memory?: (typeof VALID_MEMORY_OPTIONS)[number] | Expression<number> | ResetValue;
    /**
     * Timeout for the function in seconds, possible values are 0 to 540.
     */
    timeoutSeconds?: number | Expression<number> | ResetValue;
    /**
     * Min number of actual instances to be running at a given time.
     *
     * @remarks
     * Instances will be billed for memory allocation and 10% of CPU allocation
     * while idle.
     */
    minInstances?: number | Expression<number> | ResetValue;
    /**
     * Max number of actual instances allowed to be running in parallel.
     */
    maxInstances?: number | Expression<number> | ResetValue;
    /**
     * Connect cloud function to specified VPC connector.
     */
    vpcConnector?: string | Expression<string> | ResetValue;
    /**
     * Egress settings for VPC connector.
     */
    vpcConnectorEgressSettings?: (typeof VPC_EGRESS_SETTINGS_OPTIONS)[number] | ResetValue;
    /**
     * Specific service account for the function to run as.
     */
    serviceAccount?: "default" | string | Expression<string> | ResetValue;
    /**
     * Ingress settings which control where this function can be called from.
     */
    ingressSettings?: (typeof INGRESS_SETTINGS_OPTIONS)[number] | ResetValue;
    /**
     * User labels to set on the function.
     */
    labels?: Record<string, string>;
    /**
     * Invoker to set access control on https functions.
     */
    invoker?: "public" | "private" | string | string[];
    secrets?: (string | SecretParam)[];
    /**
     * Determines whether Firebase AppCheck is enforced.
     *
     * @remarks
     * When true, requests with invalid tokens autorespond with a 401
     * (Unauthorized) error.
     * When false, requests with invalid tokens set context.app to undefiend.
     */
    enforceAppCheck?: boolean;
    /**
     * Determines whether Firebase App Check token is consumed on request. Defaults to false.
     *
     * @remarks
     * Set this to true to enable the App Check replay protection feature by consuming the App Check token on callable
     * request. Tokens that are found to be already consumed will have request.app.alreadyConsumed property set true.
     *
     *
     * Tokens are only considered to be consumed if it is sent to the App Check service by setting this option to true.
     * Other uses of the token do not consume it.
     *
     * This replay protection feature requires an additional network call to the App Check backend and forces the clients
     * to obtain a fresh attestation from the chosen attestation providers. This can therefore negatively impact
     * performance and can potentially deplete your attestation providers' quotas faster. Use this feature only for
     * protecting low volume, security critical, or expensive operations.
     *
     * This option does not affect the enforceAppCheck option. Setting the latter to true will cause the callable function
     * to automatically respond with a 401 Unauthorized status code when request includes an invalid App Check token.
     * When request includes valid but consumed App Check tokens, requests will not be automatically rejected. Instead,
     * the request.app.alreadyConsumed property will be set to true and pass the execution to the handler code for making
     * further decisions, such as requiring additional security checks or rejecting the request.
     */
    consumeAppCheckToken?: boolean;
    /**
     * Controls whether function configuration modified outside of function source is preserved. Defaults to false.
     *
     * @remarks
     * When setting configuration available in the underlying platform that is not yet available in the Firebase Functions
     * SDK, we highly recommend setting `preserveExternalChanges` to `true`. Otherwise, when the Firebase Functions SDK releases
     * a new version of the SDK with support for the missing configuration, your function's manually configured setting
     * may inadvertently be wiped out.
     */
    preserveExternalChanges?: boolean;
}

各種説明

自身の今後のために、ChatGPTの手を借りつつ一覧にまとめてみました。
ちょっと謎な表現があったりしますが、ご容赦ください。

パラメータ 説明
failurePolicy 関数の失敗ポリシーを設定します。true は空のリトライオブジェクトを提供することに相当します。
memory 関数に割り当てるメモリの量を指定します。
timeoutSeconds 関数のタイムアウトを秒単位で設定します。可能な値は0から540までです。
minInstances 一定時間に実行されるべき最小インスタンス数を指定します。インスタンスはアイドル時にメモリ割り当てとCPU割り当ての10%で課金されます。
maxInstances 同時に実行される最大インスタンス数を指定します。
vpcConnector クラウド関数を特定のVPCコネクタに接続するために使用します。
vpcConnectorEgressSettings VPCコネクタのエグレス設定を指定します。
serviceAccount 関数が実行される特定のサービスアカウントを設定します。
ingressSettings この関数がどこから呼び出されるかを制御するためのイングレス設定を指定します。
labels 関数に設定するユーザーラベルです。
invoker HTTPS関数のアクセス制御に使用するインボーカーを設定します。
secrets 関数で使用されるシークレットパラメータのリストです。
enforceAppCheck Firebase AppCheckの強制を決定します。true の場合、無効なトークンを持つリクエストは401エラーで自動応答します。false の場合、無効なトークンを持つリクエストは context.appundefined に設定します。
consumeAppCheckToken Firebase App Checkトークンの消費を制御します。このオプションは、コール可能なリクエストにApp Checkトークンを消費するための再生保護機能を有効にします。
preserveExternalChanges 関数ソース外で変更された関数設定が保存されるかどうかを制御します。デフォルトは false です。

ホスティングの設定

firebase.jsonで、対象のパスに用意した関数に当てましょう。
nitroの serverFunctionName から関数名を変えることは可能です。デフォルトでは server という名前が振られているのでその通りに。

{
  "hosting": {
    "public": ".output/public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "server",
        "region": "asia-northeast1"
      }
    ]
  },
  "functions": [
     ~~~
    {
      "source": ".output/server",
      "codebase": "default" # ここはご自由に
    },
    ~~~
  ]
}

おわり

以前までは regionの設定もできず、力技で解決していました。
ちょっと気持ち悪い実装だったので、ちょっとモヤモヤが晴れた気持ちです。

export default defineNuxtConfig({
  ~~~
  nitro: {
    preset: "firebase",
    replace: {
      [`functions.https.onRequest`]: `functions.region('asia-northeast1').runWith({ timeoutSeconds: 120, memory: '512MB' }).https.onRequest`,
    }
  },
  ~~~
})
筆者情報
IT業界経験6年目のフルスタックエンジニア。
フロントエンドを軸として技術を研鑽中でございます。