將第 1 代 Node.js 函式升級至第 2 代

使用第 1 代函式的應用程式應考慮按照本指南中的操作說明,遷移至第 2 代函式。第 2 代函式使用 Cloud Run,可提供更優異的效能、設定、監控等功能。

本頁面的範例假設您使用 JavaScript 和 CommonJS 模組 (require 樣式匯入),但相同原則也適用於 JavaScript 和 ESM (import … from 樣式匯入) 和 TypeScript。

遷移程序

第 1 代和第 2 代函式可以在同一個檔案中並存。這樣一來,您就能在準備就緒時,輕鬆逐一遷移。建議您一次遷移一個函式,並在繼續操作前執行測試和驗證。

確認 Firebase CLI 和 firebase-functions 版本

請確認您使用的 Firebase CLI 版本至少為 12.00firebase-functions 版本至少為 4.3.0。任何新版都會支援第 2 代和第 1 代。

更新匯入作業

第 2 代函式會從 firebase-functions SDK 的 v2 子套件匯入。 Firebase CLI 只要有這個不同的匯入路徑,就能判斷要將函式程式碼部署為第 1 代或第 2 代函式。

v2 子套件是模組化套件,建議您只匯入所需的特定模組。

變更前:第 1 代

const functions = require("firebase-functions/v1");

變更後:第 2 代

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

更新觸發條件定義

由於第 2 代 SDK 偏好模組化匯入,請更新觸發條件定義,反映上一步中變更的匯入項目。

傳遞至部分觸發條件回呼的引數已變更。在這個範例中,請注意 onDocumentCreated 回呼的引數已整合成單一 event 物件。此外,部分觸發條件還提供便利的新設定功能,例如 onRequest 觸發條件的 cors 選項。

變更前:第 1 代

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

變更後:第 2 代

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

使用參數化設定

第 2 代函式不再支援 functions.config,改為在程式碼集內以宣告方式定義設定參數,提供更安全的介面。使用新的 params 模組時,除非所有參數都有有效值,否則 CLI 會封鎖部署作業,確保函式不會在缺少設定的情況下部署。

遷移至 params 子套件

如果您一直使用 functions.config 進行環境設定,可以將現有設定遷移至參數化設定

變更前:第 1 代

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

變更後:第 2 代

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

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

設定參數值

首次部署時,Firebase CLI 會提示您輸入所有參數的值,並將這些值儲存到 dotenv 檔案。如要匯出 functions.config 值,請執行 firebase functions:config:export

為進一步確保安全,您也可以指定參數類型驗證規則

特殊情況:API 金鑰

params 模組與 Cloud Secret Manager 整合,可針對 API 金鑰等機密值提供精細的存取權控管。詳情請參閱密鑰參數

變更前:第 1 代

const functions = require("firebase-functions/v1");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

變更後:第 2 代

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

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

設定執行階段選項

第 1 代和第 2 代的執行階段選項設定有所不同。第 2 代也新增了為所有函式設定選項的功能。

變更前:第 1 代

const functions = require("firebase-functions/v1");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

變更後:第 2 代

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

使用並行

第 2 代函式的一大優勢是單一函式執行個體可同時處理多個要求。這項做法可大幅減少使用者遇到的冷啟動次數。並行數預設為 80,但您可以將其設為 1 到 1000 之間的任何值:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

調整並行數可提升函式效能並降低成本。如要進一步瞭解並行,請參閱「允許並行要求」。

稽核全域變數用量

撰寫第 1 代函式時若未考慮並行,可能會使用在每個要求中設定及讀取的全域變數。啟用並行功能後,單一執行個體會同時處理多項要求,這可能會在函式中產生錯誤,因為並行要求會同時設定及讀取全域變數。

升級時,您可以將函式的 CPU 設為 gcf_gen1,並將 concurrency 設為 1,還原第 1 代的行為:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

不過,我們不建議長期採用這種做法,因為這樣會失去第 2 代函式的效能優勢。請改為稽核函式中的全域變數用量,並在準備就緒時移除這些暫時設定。

將流量遷移至新的第 2 代函式

如同變更函式的區域或觸發類型時,您需要為第 2 代函式提供新名稱,並逐步將流量遷移至該函式。

您無法升級函式,將第 1 代函式升級為第 2 代函式,並使用相同名稱和執行 firebase deploy。這麼做會導致下列錯誤:

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

請先確認函式為等冪,再按照下列步驟操作,因為在變更期間,新舊版本的函式會同時執行。舉例來說,如果您有回應 Firestore 寫入事件的第 1 代函式,請確保回應這些事件時,第 1 代函式和第 2 代函式各回應一次,應用程式仍會保持一致狀態。

  1. 在函式程式碼中重新命名函式。舉例來說,將 resizeImage 重新命名為 resizeImageSecondGen
  2. 部署函式,讓原始第 1 代函式和第 2 代函式都能執行。
    1. 如果是可呼叫、工作佇列和 HTTP 觸發條件,請更新用戶端程式碼,將所有用戶端指向第 2 代函式的名稱或網址,開始使用第 2 代函式。
    2. 使用背景觸發條件時,第 1 代和第 2 代函式都會在部署後立即回應每個事件。
  3. 所有流量都移轉完畢後,請使用 Firebase CLI 的 firebase functions:delete 指令刪除第 1 代函式。
    1. 或者,您也可以重新命名第 2 代函式,使其名稱與第 1 代函式相同。