設定環境


您通常需要函式的額外設定,例如第三方 API 金鑰或可調整的設定。Cloud Functions 專用的 Firebase SDK 提供內建的環境設定,方便您為專案儲存及擷取這類類型的資料。

選項有以下三種:

  • 參數化設定 (適用於多數情況)。這可提供強型環境設定,其中包含在部署時驗證的參數,以防止錯誤並簡化偵錯作業。
  • 環境變數的檔案型設定。如果使用這個方法,您將手動建立用於載入環境變數的 dotenv 檔案。
  • 使用 Firebase CLI 和 functions.config (僅限 Cloud Functions (第 1 代)) 的執行階段環境設定

在大多數情況下,建議您採用參數化設定。此方法可在執行階段和部署期間提供設定值,並禁止部署,除非所有參數都具備有效值。相反地,在部署期間無法使用環境變數的設定。

參數化設定

Cloud Functions for Firebase 提供介面,可讓您在程式碼集中透過宣告定義設定參數。這些參數的值可在函式部署期間、設定部署和執行階段選項,以及在執行期間使用。這表示除非所有參數具備有效的值,否則 CLI 會封鎖部署作業。

Node.js

const { onRequest } = require('firebase-functions/v2/https');
const { defineInt, defineString } = require('firebase-functions/params');

// Define some parameters
const minInstancesConfig = defineInt('HELLO_WORLD_MININSTANCES');
const welcomeMessage = defineString('WELCOME_MESSAGE');

// To use configured parameters inside the config for a function, provide them
// directly. To use them at runtime, call .value() on them.
export const helloWorld = onRequest(
  { minInstances: minInstancesConfig },
(req, res) => {
    res.send(`${welcomeMessage.value()}! I am a function.`);
  }
);

Python

from firebase_functions import https_fn
from firebase_functions.params import IntParam, StringParam

MIN_INSTANCES = IntParam("HELLO_WORLD_MIN_INSTANCES")
WELCOME_MESSAGE = StringParam("WELCOME_MESSAGE")

# To use configured parameters inside the config for a function, provide them
# directly. To use them at runtime, call .value() on them.
@https_fn.on_request(min_instances=MIN_INSTANCES)
def hello_world(req):
    return https_fn.Response(f'{WELCOME_MESSAGE.value()}! I am a function!')

部署具有參數化設定變數的函式時,Firebase CLI 會先嘗試從本機 .env 檔案載入其值。如果這些檔案中並未包含這些檔案,且未設定 default,CLI 會在部署期間提示輸入值,然後將值自動儲存至 functions/ 目錄中名為 .env.<project_ID>.env 檔案:

$ firebase deploy
i  functions: preparing codebase default for deployment
? Enter a string value for ENVIRONMENT: prod
i  functions: Writing new parameter values to disk: .env.projectId
…
$ firebase deploy
i  functions: Loaded environment variables from .env.projectId

視開發工作流程而定,建議您將產生的 .env.<project_ID> 檔案新增至版本管控系統。

在全域範圍使用參數

在部署期間,系統會先載入並檢查函式程式碼,然後才有實際值。也就是說,在全域範圍中擷取參數值會導致部署失敗。如要使用參數初始化全域值,請使用初始化回呼 onInit()。此回呼會在任何函式於實際工作環境中執行之前執行,但不會在部署期間呼叫,因此可以安全存取參數值。

Node.js

const { GoogleGenerativeAI } = require('@google/generative-ai');
const { defineSecret } = require('firebase-functions/params');
const { onInit } = require('firebase-functions/v2/core');

const apiKey = defineSecret('GOOGLE_API_KEY');

let genAI;
onInit(() => {
  genAI = new GoogleGenerativeAI(apiKey.value());
})

Python

from firebase_functions.core import init
from firebase_functions.params import StringParam, PROJECT_ID
import firebase_admin
import vertexai

location = StringParam("LOCATION")

x = "hello"

@init
def initialize():
  # Note: to write back to a global, you'll need to use the "global" keyword
  # to avoid creating a new local with the same name.
  global x
  x = "world"
  firebase_admin.initialize_app()
  vertexai.init(PROJECT_ID.value, location.value)

請注意,如果您使用 Secret 類型的參數,這些參數僅適用於已繫結密鑰的函式程序。如果只有某些函式繫結了密鑰,請在使用前檢查 secret.value() 是否真偽。

設定 CLI 行為

您可以使用 Options 物件設定參數,該物件控管 CLI 會如何提示值。以下範例設定驗證電話號碼格式、提供簡易選項選項,以及從 Firebase 專案自動填入選取選項的選項:

Node.js

const { defineString } = require('firebase-functions/params');

const welcomeMessage = defineString('WELCOME_MESSAGE', {default: 'Hello World',
description: 'The greeting that is returned to the caller of this function'});

const onlyPhoneNumbers = defineString('PHONE_NUMBER', {
  input: {
    text: {
      validationRegex: /\d{3}-\d{3}-\d{4}/,
      validationErrorMessage: "Please enter
a phone number in the format XXX-YYY-ZZZZ"
    },
  },
});

const selectedOption = defineString('PARITY', {input: params.select(["odd", "even"])});

const memory = defineInt("MEMORY", {
  description: "How much memory do you need?",
  input: params.select({ "micro": 256, "chonky": 2048 }),
});

const extensions = defineList("EXTENSIONS", {
  description: "Which file types should be processed?",
  input: params.multiSelect(["jpg", "tiff", "png", "webp"]),
});

const storageBucket = defineString('BUCKET', {
  description: "This will automatically
populate the selector field with the deploying Cloud Project’s
storage buckets",
  input: params.PICK_STORAGE_BUCKET,
});

Python

from firebase_functions.params import (
    StringParam,
    ListParam,
    TextInput,
    SelectInput,
    SelectOptions,
    ResourceInput,
    ResourceType,
)

MIN_INSTANCES = IntParam("HELLO_WORLD_MIN_INSTANCES")

WELCOME_MESSAGE = StringParam(
    "WELCOME_MESSAGE",
    default="Hello World",
    description="The greeting that is returned to the caller of this function",
)

ONLY_PHONE_NUMBERS = StringParam(
    "PHONE_NUMBER",
    input=TextInput(
        validation_regex="\d{3}-\d{3}-\d{4}",
        validation_error_message="Please enter a phone number in the format XXX-YYY-XXX",
    ),
)

SELECT_OPTION = StringParam(
    "PARITY",
    input=SelectInput([SelectOptions(value="odd"), SelectOptions(value="even")]),
)

STORAGE_BUCKET = StringParam(
    "BUCKET",
    input=ResourceInput(type=ResourceType.STORAGE_BUCKET),
    description="This will automatically populate the selector field with the deploying Cloud Project's storage buckets",
)

參數類型

參數化設定可為參數值提供強大的輸入機制,並支援 Cloud Secret Manager 中的密鑰。支援的類型如下:

  • 密鑰
  • 字串
  • 布林值
  • 整數
  • 浮點值
  • 清單 (Node.js)

參數值和運算式

Firebase 會在部署時間和函式執行期間評估參數。由於這種雙重環境,您在比較參數值,以及使用這些參數設定函式的執行階段選項時,必須格外小心。

如要將參數做為執行階段選項傳遞至函式,請直接傳送參數:

Node.js

const { onRequest } = require('firebase-functions/v2/https');
const { defineInt } = require('firebase-functions/params');
const minInstancesConfig = defineInt('HELLO\_WORLD\_MININSTANCES');

export const helloWorld = onRequest(
  { minInstances: minInstancesConfig },
  (req, res) => {
    //…

Python

from firebase_functions import https_fn
from firebase_functions.params import IntParam

MIN_INSTANCES = IntParam("HELLO_WORLD_MIN_INSTANCES")

@https_fn.on_request(min_instances=MIN_INSTANCES)
def hello_world(req):
    ...

此外,如果您需要與參數進行比較,才能知道該挑選哪個選項,則必須使用內建的比較子,而不是檢查值:

Node.js

const { onRequest } = require('firebase-functions/v2/https');
const environment = params.defineString(‘ENVIRONMENT’, {default: 'dev'});

// use built-in comparators
const minInstancesConfig = environment.equals('PRODUCTION').thenElse(10, 1);
export const helloWorld = onRequest(
  { minInstances: minInstancesConfig },
  (req, res) => {
    //…

Python

from firebase_functions import https_fn
from firebase_functions.params import IntParam, StringParam

ENVIRONMENT = StringParam("ENVIRONMENT", default="dev")
MIN_INSTANCES = ENVIRONMENT.equals("PRODUCTION").then(10, 0)

@https_fn.on_request(min_instances=MIN_INSTANCES)
def hello_world(req):
    ...

只能在執行階段使用的參數和參數運算式,可以透過 value 函式存取:

Node.js

const { onRequest } = require('firebase-functions/v2/https');
const { defineString } = require('firebase-functions/params');
const welcomeMessage = defineString('WELCOME_MESSAGE');

// To use configured parameters inside the config for a function, provide them
// directly. To use them at runtime, call .value() on them.
export const helloWorld = onRequest(
(req, res) => {
    res.send(`${welcomeMessage.value()}! I am a function.`);
  }
);

Python

from firebase_functions import https_fn
from firebase_functions.params import StringParam

WELCOME_MESSAGE = StringParam("WELCOME_MESSAGE")

@https_fn.on_request()
def hello_world(req):
    return https_fn.Response(f'{WELCOME_MESSAGE.value()}! I am a function!')

內建參數

Cloud Functions SDK 提供三個預先定義的參數,可透過 firebase-functions/params 子套件取得:

Node.js

  • projectID:執行函式的 Cloud 專案。
  • databaseURL:與該函式相關聯的即時資料庫執行個體網址 (如果在 Firebase 專案中啟用)。
  • storageBucket:與函式相關聯的 Cloud Storage 值區 (如果在 Firebase 專案中啟用)。

Python

  • PROJECT_ID:執行函式的 Cloud 專案。
  • DATABASE_URL:與該函式相關聯的即時資料庫執行個體網址 (如果在 Firebase 專案中啟用)。
  • STORAGE_BUCKET:與函式相關聯的 Cloud Storage 值區 (如果在 Firebase 專案中啟用)。

這些函式就像使用者定義的字串參數,在任何方面都一樣,但由於其值一律會顯示在 Firebase CLI 中,因此值不會在部署時提示輸入,也不會儲存至 .env 檔案。

密鑰參數

使用 defineSecret() 定義的 Secret 類型參數代表含有儲存在 Cloud Secret Manager 值的字串參數。請在 Cloud Secret Manager 中檢查密鑰參數是否存在,並在部署期間透過互動方式提示新密鑰的值,而不要檢查本機的 .env 檔案並在缺少時寫入新的值。

以這種方式定義的密鑰參數必須繫結至個別應有存取權的函式:

Node.js

const { onRequest } = require('firebase-functions/v2/https');
const { defineSecret } = require('firebase-functions/params');
const discordApiKey = defineSecret('DISCORD_API_KEY');

export const postToDiscord = onRequest(
  { secrets: [discordApiKey] },
  (req, res) => {
  const apiKey = discordApiKey.value();
    //…

Python

from firebase_functions import https_fn
from firebase_functions.params import SecretParam

DISCORD_API_KEY = SecretParam('DISCORD_API_KEY')

@https_fn.on_request(secrets=[DISCORD_API_KEY])
def post_to_discord(req):
    api_key = DISCORD_API_KEY.value

由於密鑰值會一直隱藏到執行函式為止,因此在設定函式時無法使用。

環境變數

Cloud Functions for Firebase 支援使用 dotenv 檔案格式,將 .env 檔案中指定的環境變數載入至應用程式的執行階段。部署完成後,您可以透過 process.env 介面 (以 Node.js 為基礎的專案) 或 os.environ (在 Python 式專案中讀取) 讀取環境變數。

如要以這種方式設定環境,請在專案中建立 .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.
    # ...
    

部署自訂環境變數後,您的函式程式碼就能存取這些變數:

Node.js

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

Python

import os

@https_fn.on_request()
def hello(req):
    return https_fn.Response(
        f"Hello {os.environ.get('PLANET')} and {os.environ.get('AUDIENCE')}"
    )

部署多組環境變數

如果您需要 Firebase 專案另一組環境變數 (例如測試環境和實際工作環境),請建立 .env.<project or alias> 檔案,並在其中寫入專案專屬的環境變數。.env 的環境變數和專案專屬 .env 檔案 (如有) 都會包含在所有已部署的函式中。

舉例來說,專案可能包含下列三個檔案,其中包含的開發和實際工作環境略有不同的值:

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

AUDIENCE=人類

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 專案
  • GCLOUD_PROJECT
  • GOOGLE_CLOUD_PROJECT
  • FUNCTION_TRIGGER_TYPE
  • FUNCTION_NAME
  • FUNCTION_MEMORY_MB
  • FUNCTION_TIMEOUT_SEC
  • FUNCTION_IDENTITY
  • 區域
  • FUNCTION_TARGET
  • FUNCTION_SIGNATURE_TYPE
  • K_SERVICE
  • K_REVISION
  • 通訊埠
  • K_設定

儲存及存取機密設定資訊

儲存在 .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 參數存取密鑰:

    Node.js

    const { onRequest } = require('firebase-functions/v2/https');
    
    exports.processPayment = onRequest(
      { secrets: ["SECRET_NAME"] },
      (req, res) => {
        const myBillingService = initializeBillingService(
          // reference the secret value
          process.env.SECRET_NAME
        );
        // Process the payment
      }
    );

    Python

    import os
    from firebase_functions import https_fn
    
    @https_fn.on_request(secrets=["SECRET_NAME"])
    def process_payment(req):
        myBillingService = initialize_billing(key=os.environ.get('SECRET_NAME'))
        # Process the payment
        ...
    
  4. 部署 Cloud Functions:

    firebase deploy --only functions

    您現在可以以任何其他環境變數相同的方式存取該變數。 相反地,如有其他未在 runWith 中指定密鑰的函式嘗試存取該密鑰,則會接收未定義的值:

    Node.js

    exports.anotherEndpoint = 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
    });
    

    Python

    @https_fn.on_request()
    def another_endpoint(req):
        return https_fn.Response(f"The secret API key is {os.environ.get("SECRET_NAME")}")
        # Responds with "The secret API key is None" because the `secrets` 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

對於 accessdestroy 指令,您可以提供選用的版本參數來管理特定版本。例如:

functions:secrets:access SECRET_NAME[@VERSION]

如要進一步瞭解這些作業,請透過指令傳遞 -h 以查看 CLI 說明。

密鑰的計費方式

Secret Manager 允許 6 個有效的密鑰版本,無須額外付費。這表示在 Firebase 專案中,每月可免費使用 6 個密鑰。

根據預設,Firebase CLI 在適當情況下會嘗試自動刪除未使用的密鑰版本,例如使用新版密鑰部署函式時。此外,您可以使用 functions:secrets:destroyfunctions:secrets:prune 主動清除未使用的密鑰。

Secret Manager 允許對 Secret Manager 每月 10,000 個未計費的存取作業。每當函式執行個體冷啟動時,就只會讀取 runWith 參數中指定的密鑰。如果您有許多函式執行個體讀取大量密鑰,專案可能會超出這個上限,屆時每 10,000 次存取作業費用為 $0.03 美元。

詳情請參閱 Secret Manager 定價

模擬器支援

採用 dotenv 的環境設定旨在與本機 Cloud Functions 模擬器互通。

使用本機 Cloud Functions 模擬器時,您可以設定 .env.local 檔案來覆寫專案的環境變數。.env.local 的內容優先順序高於 .env 和專案專屬的 .env 檔案。

舉例來說,專案可能包含下列三個檔案,其中包含開發和本機測試的值略有不同的:

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

AUDIENCE=人類

AUDIENCE=Dev Humans AUDIENCE=當地人

在本機環境中啟動時,模擬器會載入環境變數,如下所示:

  $ 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 提供匯出指令,會以 .env 檔案的形式,輸出目錄 .firebaserc 檔案 (如下方範例、localdevprod) 中所列的每個別名或專案。

如要遷移,請使用 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,如環境變數中所示。