Check out what’s new from Firebase at Google I/O 2022. Learn more

環境を構成する

サードパーティの API キーや調整可能な設定など、関数に追加の構成が必要なことがよくあります。Firebase SDK for Cloud Functions では、プロジェクトのこのタイプのデータを簡単に保存および取得できるように、環境構成が組み込まれています。

ファイルベースの環境変数の構成(推奨)、または Firebase CLI と functions.config を使用するランタイム環境構成のいずれかを選択できます。このガイドでは両方の方法について説明します。

環境変数

Cloud Functions for Firebase は、.env ファイルで指定した環境変数をアプリケーション ランタイムに読み込むために、dotenv ファイル形式をサポートしています。デプロイした後は、環境変数は process.env インターフェースを通じて読み取ることができます。

この方法で環境を構成するには、プロジェクトに .env ファイルを作成し、必要な変数を追加してデプロイします。

  1. functions/ ディレクトリに .env ファイルを作成します。

    # Directory layout:
    #   my-project/
    #     firebase.json
    #     functions/
    #       .env
    #       package.json
    #       index.js
    
  2. .env ファイルを開いて編集し、必要なキーを追加します。次に例を示します。

    PLANET=Earth
    AUDIENCE=Humans
    
  3. 関数をデプロイして、環境変数が読み込まれることを確認します。

    firebase deploy --only functions
    # ...
    # i functions: Loaded environment variables from .env.
    # ...
    

カスタム環境変数をデプロイした後、関数コードは process.env 構文を使用してカスタム環境変数にアクセスできます。

// Responds with "Hello Earth and Humans"
exports.hello = functions.https.onRequest((request, response) => {
  response.send(`Hello ${process.env.PLANET} and ${process.env.AUDIENCE}`);
});

環境変数の複数のセットのデプロイ

Firebase プロジェクトで環境変数の複数のセットが必要になる場合は(ステージング環境と本番環境など)、.env.<project or alias> ファイルを作成し、そこにプロジェクト固有の環境変数を記述します。.env の環境変数と、プロジェクト固有の .env ファイル(存在する場合)の環境変数が読み込まれ、デプロイされるすべての関数で利用できます。

たとえば、開発用と本番環境用でわずかに異なる値が含まれる 3 つのファイルを、1 つのプロジェクトに含めることができます。

.env .env.dev .env.prod
PLANET=Earth

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Prod Humans

これらの個別のファイルに値があるため、関数とともにデプロイされる環境変数のセットは、ターゲット プロジェクトによって異なります。

$ firebase use dev
$ firebase deploy --only functions
i functions: Loaded environment variables from .env, .env.dev.
# Deploys functions with following user-defined environment variables:
#   PLANET=Earth
#   AUDIENCE=Dev Humans

$ firebase use prod
$ firebase deploy --only functions
i functions: Loaded environment variables from .env, .env.prod.
# Deploys functions with following user-defined environment variables:
#   PLANET=Earth
#   AUDIENCE=Prod Humans

予約済みの環境変数

一部の環境変数キーは内部使用のために予約されています。.env ファイル内で以下のキーを使用しないでください。

  • 先頭が X_GOOGLE_ であるすべてのキー
  • 先頭が EXT_ であるすべてのキー
  • 先頭が FIREBASE_ であるすべてのキー
  • 次のリストにあるキー:
  • CLOUD_RUNTIME_CONFIG
  • ENTRY_POINT
  • GCP_PROJECT
  • GCLOUD_PROJECT
  • GOOGLE_CLOUD_PROJECT
  • FUNCTION_TRIGGER_TYPE
  • FUNCTION_NAME
  • FUNCTION_MEMORY_MB
  • FUNCTION_TIMEOUT_SEC
  • FUNCTION_IDENTITY
  • FUNCTION_REGION
  • FUNCTION_TARGET
  • FUNCTION_SIGNATURE_TYPE
  • K_SERVICE
  • K_REVISION
  • PORT
  • K_CONFIGURATION

機密性の高い構成情報の保存とアクセス

.env ファイルに環境変数を保存してそれらを関数の構成に使用できますが、これはデータベースの認証情報や API キーなどの機密性の高い情報を安全に保存する方法ではありません。このことは、.env ファイルをソース管理にチェックインする場合に特に重要です。

機密性の高い構成情報を保存するため、Cloud Functions for Firebase は Google Cloud Secret Manager と統合されています。この暗号化されたサービスは、構成値を安全に保存すると同時に、必要に応じて関数から簡単にアクセスできるようにもしています。

シークレットの作成と使用

シークレットを作成するには、Firebase CLI を使用します。

シークレットを作成して使用するには:

  1. ローカル プロジェクト ディレクトリのルートから次のコマンドを実行します。

    firebase functions:secrets:set SECRET_NAME

  2. SECRET_NAME の値を入力します。

    CLI に成功を示すメッセージがエコーされ、変更を有効にするには関数をデプロイする必要があるという警告が表示されます。

  3. デプロイする前に、関数コードで runWith パラメータを使用してシークレットにアクセスできるようにしていることを確認してください。

    exports.processPayment = functions
      // Make the secret available to this function
      .runWith({ secrets: ["SECRET_NAME"] })
      .onCall((data, context) => {
        const myBillingService = initializeBillingService(
          // reference the secret value
          process.env.SECRET_NAME
        );
        // Process the payment
      });
  4. Cloud Functions の関数をデプロイします。

    firebase deploy --only functions

これで、他の環境変数と同様にシークレットにアクセスできるようになります。runWith でシークレットを指定していない別の関数がシークレットにアクセスしようとすると、未定義の値が返されます。

exports.anotherEndpoint = functions.https.onRequest((request, response) => {
  response.send(`The secret API key is ${process.env.SECRET_NAME}`);
  // responds with "The secret API key is undefined" because the `runWith` parameter is missing
});

関数がデプロイされると、シークレット値にアクセスできるようになります。runWith パラメータにシークレットを明示的に含んでいる関数だけが、環境変数としてそのシークレットにアクセスできます。これにより、シークレット値は必要な場合のみ利用可能になるため、誤ってシークレットが漏洩するリスクを軽減できます。

シークレットの管理

シークレットの管理には Firebase CLI を使用します。この方法でシークレットを管理できますが、CLI で変更を行うと、場合によっては関連する関数の変更や再デプロイが必要になることに注意してください。特に、以下の点に注意してください。

  • シークレットに新しい値を設定した場合、関数で新しい値を取得するには、そのシークレットを参照するすべての関数を再デプロイする必要があります。
  • シークレットを削除する場合は、デプロイされている関数の中に、そのシークレットを参照しているものがないことを確認してください。削除されたシークレット値を使用する関数は正しく動作せず、その旨が通知されることもありません。

シークレットを管理するための Firebase CLI コマンドの概要は次のとおりです。

# Change the value of an existing secret
firebase functions:secrets:set SECRET_NAME

# View the value of a secret
functions:secrets:access SECRET_NAME

# Destroy a secret
functions:secrets:destroy SECRET_NAME

# View all secret versions and their state
functions:secrets:get SECRET_NAME

# Automatically clean up all secrets that aren't referenced by any of your functions
functions:secrets:prune

access コマンドと destroy コマンドでは、オプションのバージョン パラメータを指定することによって、特定のバージョンを管理できます。次に例を示します。

functions:secrets:access SECRET_NAME[@VERSION]

これらのオペレーションの詳細については、コマンドで -h を渡すと CLI のヘルプが表示されます。

シークレットの課金の仕組み

Secret Manager では、6 個のアクティブなシークレット バージョンを無料で使用できます。つまり、1 つの Firebase プロジェクトで 1 か月あたり 6 個のシークレットを無料で使用できます。

デフォルトでは、Firebase CLI は状況に応じて未使用のシークレット バージョンを自動的に破棄することを試みます。たとえば、新しいバージョンのシークレットを使用する関数をデプロイする場合などがこの状況に相当します。また、functions:secrets:destroyfunctions:secrets:prune を使用して、未使用のシークレットを自らクリーンアップすることもできます。

Secret Manager では、1 つのシークレットに対して毎月 10,000 回のアクセス オペレーションを請求なしで使用できます。関数インスタンスは、コールド スタートするたびに、runWith パラメータで指定されたシークレットのみを読み取ります。多くの関数インスタンスがあり、多くのシークレットを読み取る場合、プロジェクトでこの割り当て量を超過する可能性があります。超過した時点から、アクセス オペレーション 10,000 回あたり $0.03 が課金されます。

詳細については、Secret Manager の料金をご覧ください。

エミュレータのサポート

dotenv を使用する環境構成は、ローカルの Cloud Functions エミュレータと相互運用するように設計されています。

ローカルの Cloud Functions エミュレータを使用する場合は、.env.local ファイルを設定することで、プロジェクトの環境変数をオーバーライドできます。.env.local の内容は、.env ファイルおよびプロジェクト固有の .env ファイルよりも優先されます。

たとえば、開発用とローカルテスト用にわずかに異なる値が含まれる 3 つのファイルを 1 つのプロジェクトに含めたとします。

.env .env.dev .env.local
PLANET=Earth

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Local Humans

ローカル コンテキストで起動された場合、エミュレータが読み込む環境変数は次のようになります。

  $ firebase emulators:start
  i  emulators: Starting emulators: functions
  # Starts emulator with following environment variables:
  #  PLANET=Earth
  #  AUDIENCE=Local Humans

Cloud Functions エミュレータ内のシークレットと認証情報

Cloud Functions エミュレータは、機密性の高い構成情報の保存とアクセスを可能にするために、シークレットの使用をサポートします。デフォルトでは、エミュレータはアプリケーションのデフォルト認証情報を使用して、本番環境のシークレットにアクセスしようと試みます。CI 環境などの特定の状況では、権限の制限により、エミュレータがシークレット値にアクセスできない場合があります。

Cloud Functions エミュレータによる環境変数のサポートと同様に、.secret.local ファイルを設定してシークレット値をオーバーライドすることができます。これにより、実際のシークレット値にアクセスできない場合であっても、関数をローカルで簡単にテストできます。

環境構成からの移行

functions.config で環境構成を使用している場合は、既存の構成を環境変数(dotenv 形式)として移行できます。Firebase CLI には、ディレクトリの .firebaserc ファイルに記載されている各エイリアスまたはプロジェクトの構成(次の例では localdevprod)を .env ファイルとして出力するエクスポート コマンドが用意されています。

移行するには、firebase functions:config:export コマンドを使用して既存の環境構成をエクスポートします。

firebase functions:config:export
i  Importing configs from projects: [project-0, project-1]
⚠  The following configs keys could not be exported as environment variables:

⚠  project-0 (dev):
    1foo.a => 1FOO\_A (Key 1FOO\_A must start with an uppercase ASCII letter or underscore, and then consist of uppercase ASCII letters, digits, and underscores.)

Enter a PREFIX to rename invalid environment variable keys: CONFIG\_
✔  Wrote functions/.env.prod
✔  Wrote functions/.env.dev
✔  Wrote functions/.env.local
✔  Wrote functions/.env

場合によっては、エクスポートする環境変数のキーの名前を変更するために、接頭辞の入力を求められることがあります。これは、構成が無効な場合や、予約済みの環境変数のキーを使用している場合などによって、すべての構成を自動で変換できるわけではないためです。

関数をデプロイしたり、.env ファイルをソース管理にチェックインしたりする前に、生成された .env ファイルの内容を十分に確認することをおすすめします。値が機密情報で漏洩することがあってはならない場合は、.env ファイルから削除し、代わりに Secret Manager で安全に保存してください。

また、関数コードを更新する必要もあります。functions.config を使用していたすべての関数は、今後は process.env を使用して環境変数を参照する必要があります。

環境構成

firebase-functions v3.18.0 で環境変数のサポートがリリースされる前は、functions.config() を使用して環境構成を行うことが推奨されていました。この方法は引き続きサポートされますが、すべての新規プロジェクトでは代わりに環境変数を使用することをおすすめします。環境変数の方が使いやすく、コードの移植性が向上するためです。

CLI で環境構成を設定する

環境データを保存するには、Firebase CLIfirebase functions:config:set コマンドを使用します。関連する構成をグループ化するために、各キーにはピリオドを使用して名前空間を指定できます。キーには小文字しか使用できないことにご注意ください。大文字は使用できません。

たとえば、「Some Service」のクライアント ID と API キーを保存するには、以下を実行します。

firebase functions:config:set someservice.key="THE API KEY" someservice.id="THE CLIENT ID"

現在の環境構成を取得する

プロジェクトの環境構成に現在保存されているものを調べるには、firebase functions:config:get を使用します。以下のような JSON が出力されます。

{
  "someservice": {
    "key":"THE API KEY",
    "id":"THE CLIENT ID"
  }
}

この関数は Google Cloud Runtime Configuration API に基づいています。

functions.config を使用して関数で環境構成にアクセスする

一部の構成は、予約された firebase 名前空間の下で自動的に提供されます。環境構成は functions.config() を介して実行中の関数内で使用可能になります。上記の構成を使用する場合、コードは以下のようになります。

const functions = require('firebase-functions');
const request = require('request-promise');

exports.userCreated = functions.database.ref('/users/{id}').onWrite(event => {
  let email = event.data.child('email').val();

  return request({
    url: 'https://someservice.com/api/some/call',
    headers: {
      'X-Client-ID': functions.config().someservice.id,
      'Authorization': `Bearer ${functions.config().someservice.key}`
    },
    body: {email: email}
  });
});

環境構成を使用してモジュールを初期化する

一部の Node モジュールは、構成しなくても準備ができています。バックエンドに接続し、正しく初期化するために追加の構成が必要なモジュールもあります。この構成を、ハードコード化するのではなく、環境変数に保存することをおすすめします。これにより、コードを移植性の高い状態に保ち、アプリケーションをオープンソースにしたり、本番バージョンとステージング バージョンを簡単に切り替えたりすることができます。

たとえば、Slack Node SDK モジュールを使用するには、次のように記述します。

const functions = require('firebase-functions');
const IncomingWebhook = require('@slack/client').IncomingWebhook;
const webhook = new IncomingWebhook(functions.config().slack.url);

デプロイする前に、以下の slack.url 環境構成変数を設定します。

firebase functions:config:set slack.url=https://hooks.slack.com/services/XXX

その他の環境コマンド

  • firebase functions:config:unset key1 key2 は、指定されたキーを構成から削除します。
  • firebase functions:config:clone --from <fromProject> は、別のプロジェクトの環境を現在アクティブなプロジェクトにクローンします。

自動的に設定される環境変数

関数のランタイムやローカルでエミュレートされる関数で自動的に設定される環境変数があります。これには、Google Cloud によって設定される環境変数と、次の Firebase 固有の環境変数があります。

process.env.FIREBASE_CONFIG: 次のような Firebase プロジェクトの構成情報を提供します。

{
  databaseURL: 'https://databaseName.firebaseio.com',
  storageBucket: 'projectId.appspot.com',
  projectId: 'projectId'
}

引数を指定せずに Firebase Admin SDK を初期化すると、この構成が自動的に適用されます。関数を JavaScript で記述する場合は、次のように初期化します。

const admin = require('firebase-admin');
admin.initializeApp();

関数を TypeScript で作成する場合は、次のように初期化します。

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import 'firebase-functions';
admin.initializeApp();

サービス アカウントの認証情報を使用して、デフォルトのプロジェクト構成の Admin SDK を初期化する必要がある場合は、次のようにファイルから認証情報を読み込み、FIREBASE_CONFIG に追加します。

serviceAccount = require('./serviceAccount.json');

const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG);
adminConfig.credential = admin.credential.cert(serviceAccount);
admin.initializeApp(adminConfig);