Upgrade 1st gen Node.js functions to 2nd gen

Apps currently using 1st gen functions should consider migrating to 2nd gen using the instructions in this guide. 2nd gen functions use Cloud Run to provide better performance, better configuration, better monitoring, and more.

The examples in this page assume you're using JavaScript with CommonJS modules (require style imports), but the same principles apply to JavaScript with ESM (import … from style imports) and TypeScript.

The migration process

1st gen and 2nd gen functions can coexist side-by-side in the same file. This allows for easy migration piece by piece, as you're ready. We recommend migrating one function at a time, performing testing and verification before proceeding.

Verify Firebase CLI and firebase-functions versions

Make sure you're using at least Firebase CLI version 12.00 and firebase-functions version 4.3.0. Any newer version will support 2nd gen as well as 1st gen.

Update imports

2nd gen functions import from the v2 subpackage in the firebase-functions SDK. This different import path is all the Firebase CLI needs to determine whether to deploy your function code as a 1st or 2nd gen function.

The v2 subpackage is modular, and we recommend only importing the specific module that you need.

Before: 1st gen

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

After: 2nd gen

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

Update trigger definitions

Since the 2nd gen SDK favors modular imports, update trigger definitions to reflect the changed imports from the previous step.

The arguments passed to callbacks for some triggers have changed. In this example, note that the arguments to the onDocumentCreated callback have been consolidated into a single event object. Additionally, some triggers have convenient new configuration features, like the onRequest trigger's cors option.

Before: 1st gen

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

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

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

After: 2nd gen

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) => {
  /* ... */
});

Use parameterized configuration

2nd gen functions drop support for functions.config in favor of a more secure interface for defining configuration parameters declaratively inside your codebase. With the new params module, the CLI blocks deployment unless all parameters have a valid value, ensuring that a function isn't deployed with missing configuration.

Migrate to the params subpackage

If you have been using environment configuration with functions.config, you can migrate your existing configuration to parameterized configuration.

Before: 1st gen

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

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

  // ...
});

After: 2nd gen

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());

  // ...
});

Set parameter values

The first time you deploy, the Firebase CLI prompts for all values of parameters, and save the values in a dotenv file. To export your functions.config values, run firebase functions:config:export.

For additional safety, you can also specify parameter types and validation rules.

Special case: API Keys

The params module integrates with Cloud Secret Manager, which provides fine-grained access control to sensitive values like API keys. See secret parameters for more information.

Before: 1st gen

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

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

After: 2nd gen

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());
    // ...
  }
);

Set runtime options

Configuration of runtime options has changed between 1st and 2nd gen. 2nd gen also adds a new capability to set options for all functions.

Before: 1st gen

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) => {
    // ...
  });

After: 2nd gen

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) => {
  /* ... */
});

Use concurrency

A significant advantage of 2nd gen functions is the ability of a single function instance to serve more than one request at once. This can dramatically reduce the number of cold starts experienced by end users. By default, concurrency is set at 80, but you can set it to any value from 1 to 1000:

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

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

Tuning concurrency can improve performance and reduce cost of functions. Learn more about concurrency in Allow concurrent requests.

Audit global variable usage

1st gen functions written without concurrency in mind might use global variables that are set and read on each request. When concurrency is enabled and a single instance starts handling multiple requests at once, this may introduce bugs in your function as concurrent requests start setting and reading global variables simultaneously.

While upgrading, you can set your function's CPU to gcf_gen1 and set concurrency to 1 to restore 1st gen behavior:

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

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

However, this is not recommended as a long-term fix, as it forfeits the performance advantages of 2nd gen functions. Instead, audit usage of global variables in your functions, and remove these temporary settings when you're ready.

Migrate traffic to the new 2nd gen functions

Just as when changing a function's region or trigger type, you'll need to give the 2nd gen function a new name and slowly migrate traffic to it.

It is not possible to upgrade a function from 1st to 2nd gen with the same name and run firebase deploy. Doing so will result in the error:

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

Before you follow these steps, first ensure that your function is idempotent, since both the new version and the old version of your function will be running at the same time during the change. For example, if you have a 1st gen function that responds to write events in Firestore, ensure that responding to a write twice, once by the 1st gen function and once by the 2nd gen function, in response to those events leaves your app in a consistent state.

  1. Rename the function in your functions code. For example, rename resizeImage to resizeImageSecondGen.
  2. Deploy the function, so that both the original 1st gen function and 2nd gen function are running.
    1. In the case of callable, Task Queue, and HTTP triggers, begin pointing all clients to the 2nd gen function by updating client code with the 2nd gen function's name or URL.
    2. With background triggers, both the 1st gen and 2nd gen functions will respond to every event immediately upon deploy.
  3. When all traffic is migrated off, delete the 1st gen function using the firebase CLI's firebase functions:delete command.
    1. Optionally, rename the 2nd gen function to match the name of the 1st gen function.