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

Apps 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 document 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 source file. This lets you migrate your codebase piece by piece, as you're ready. Note, however, that this mixing of packages does not work within a single, discrete function .

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

Minimize rewrite efforts with JavaScript destructuring

If your functions have complex bodies that rely heavily on 1st gen context or provider-specific parameters (like message or snapshot ), you can use the 1st gen compatibility helpers built into the 2nd gen SDK.

The 2nd gen SDK automatically patches the event object with getters that match 1st gen signatures. This lets you use JavaScript destructuring to extract these properties directly in the handler signature, minimizing the need to rewrite your function logic.

Provider mapping reference

Provider 1st gen Arguments 2nd gen Patched Event Destructuring
Pub/Sub (message, context) ({ message, context }) => { ... }
Cloud Firestore (snapshot, context) ({ snapshot, context }) => { ... }
Cloud Storage (object, context) ({ object, context }) => { ... }
Realtime Database (snapshot, context) ({ snapshot, context }) => { ... }
Remote Config (version, context) ({ version, context }) => { ... }
Cloud Scheduler (context) ({ context }) => { ... }
Task Queue (data, context) ({ data, context }) => { ... }

Before (1st gen):

  export 
  
 const 
  
 myPubSubV1 
  
 = 
  
 functions 
 . 
 pubsub 
 . 
 topic 
 ( 
 "my-topic" 
 ). 
 onPublish 
 (( 
 message 
 , 
  
 context 
 ) 
  
 = 
>  
 { 
  
 const 
  
 data 
  
 = 
  
 message 
 . 
 json 
 ; 
  
 const 
  
 eventId 
  
 = 
  
 context 
 . 
 eventId 
 ; 
  
 // ... rest of the logic 
 }); 
 

New Alternative (2nd gen with Destructuring):

  import 
  
 { 
  
 onMessagePublished 
  
 } 
  
 from 
  
 "firebase-functions/v2/pubsub" 
 ; 
 export 
  
 const 
  
 myPubSubV2 
  
 = 
  
 onMessagePublished 
 ( 
 "my-topic" 
 , 
  
 ({ 
  
 message 
 , 
  
 context 
  
 }) 
  
 = 
>  
 { 
  
 // No need to change the function body! 
  
 const 
  
 data 
  
 = 
  
 message 
 . 
 json 
 ; 
  
 // Uses v1 Message wrapper 
  
 const 
  
 eventId 
  
 = 
  
 context 
 . 
 eventId 
 ; 
  
 // Uses v1 EventContext map 
  
 // ... rest of the logic 
 }); 
 

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.

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

If you have existing environment configuration with functions.config , migrate this configuration as part of your upgrade to 2nd gen.

The functions.config API is deprecated will be decommissioned in March 2027. After that date, deployments with functions.config will fail.

To prevent deployment failures, migrate your configuration to Cloud Secret Manager using the Firebase CLI. This is strongly recommended as the most efficient and secure way to migrate your configuration.

  1. Export configuration with the Firebase CLI

    Use the config export command to export your existing environment config to a new secret in Cloud Secret Manager:

     $  
    firebase  
    functions:config:export
    i  
    This  
     command 
      
    retrieves  
    your  
    Runtime  
    Config  
    values  
     ( 
    accessed  
    via  
    functions.config ()) 
      
    and  
    exports  
    them  
    as  
    a  
    Secret  
    Manager  
    secret.
    
    i  
    Fetching  
    your  
    existing  
    functions.config () 
      
    from  
    your  
    project...  
      
    Fetched  
    your  
    existing  
    functions.config () 
    .
    
    i  
    Configuration  
    to  
    be  
    exported:
    ⚠  
    This  
    may  
    contain  
    sensitive  
    data.  
    Do  
    not  
    share  
    this  
    output. { 
      
    ... } 
      
    What  
    would  
    you  
    like  
    to  
    name  
    the  
    new  
    secret  
     for 
      
    your  
    configuration?  
    RUNTIME_CONFIG
    
    ✔  
    Created  
    new  
    secret  
    version  
    projects/project/secrets/RUNTIME_CONFIG/versions/1 ``` 
     
    
  2. Update function code to bind secrets

    To use configuration stored in the new secret in Cloud Secret Manager, use the defineJsonSecret API in your function source. Also, make sure that secrets are bound to all functions that need them.

    Before

      const 
      
     functions 
      
     = 
      
     require 
     ( 
     "firebase-functions/v1" 
     ); 
     exports 
     . 
     myFunction 
      
     = 
      
     functions 
     . 
     https 
     . 
     onRequest 
     (( 
     req 
     , 
      
     res 
     ) 
      
     = 
    >  
     { 
      
     const 
      
     apiKey 
      
     = 
      
     functions 
     . 
     config 
     (). 
     someapi 
     . 
     key 
     ; 
      
     // ... 
     }); 
     
    

    After

      const 
      
     { 
      
     onRequest 
      
     } 
      
     = 
      
     require 
     ( 
     "firebase-functions/v2/https" 
     ); 
     const 
      
     { 
      
     defineJsonSecret 
      
     } 
      
     = 
      
     require 
     ( 
     "firebase-functions/params" 
     ); 
     const 
      
     config 
      
     = 
      
     defineJsonSecret 
     ( 
     "RUNTIME_CONFIG" 
     ); 
     exports 
     . 
     myFunction 
      
     = 
      
     onRequest 
     ( 
      
     // Bind secret to your function 
      
     { 
      
     secrets 
     : 
      
     [ 
     config 
     ] 
      
     }, 
      
     ( 
     req 
     , 
      
     res 
     ) 
      
     = 
    >  
     { 
      
     // Access secret values via .value() 
      
     const 
      
     apiKey 
      
     = 
      
     config 
     . 
     value 
     (). 
     someapi 
     . 
     key 
     ; 
      
     // ... 
     }); 
     
    
  3. Deploy Functions

    Deploy your updated functions to apply the changes and bind the secret permissions.

     firebase  
    deploy  
    --only  
    functions:<your-function-name> 
    

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

Update default service account (optional)

While 1st gen functions use the Google App Engine default service account to authorize access to Firebase APIs, 2nd gen functions use the Compute Engine default service account. This difference can lead to permissions issues for functions migrated to 2nd gen in cases where you have granted special permissions to the 1st gen service account. If you haven't changed any service account permissions, you can skip this step.

The recommended solution is to explicitly assign the existing 1st gen App Engine default service account to functions that you want to migrate to 2nd gen, overriding the 2nd gen default. You can do this by making sure each migrated function sets the correct value for serviceAccountEmail :

  const 
  
 { 
 onRequest 
 } 
  
 = 
  
 require 
 ( 
 "firebase-functions/https" 
 ); 
 const 
  
 { 
 onDocumentCreated 
 } 
  
 = 
  
 require 
 ( 
 "firebase-functions/v2/firestore" 
 ); 
 const 
  
 { 
 setGlobalOptions 
 } 
  
 = 
  
 require 
 ( 
 "firebase-functions" 
 ); 
 // Use the App Engine 
default service account for all functions 
 setGlobalOptions 
 ({ 
 serviceAccountEmail 
 : 
  
 '<my-project-number>@<wbr>appspot.gserviceaccount.com' 
 }); 
 // Now I use the App Engine 
default service account. 
 exports 
 . 
 date 
  
 = 
  
 onRequest 
 ({ 
 cors 
 : 
  
 true 
 }, 
  
 ( 
 req 
 , 
  
 res 
 ) 
  
 = 
>  
 { 
  
 // ... 
 }); 
 // I do too! 
 exports 
 . 
 uppercase 
  
 = 
  
 onDocumentCreated 
 ( 
 "my-collection/{docId}" 
 , 
  
 ( 
 event 
 ) 
  
 = 
>  
 { 
  
 // ... 
 }); 
 

Alternatively, you could make sure to modify the service account details to match all the necessary permissions on both the App Engine default service account (for 1st Gen) and the Compute Engine default service account (for 2nd Gen).

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, because 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. 

The migration strategy depends on the type of trigger your function uses.

Migrate Callable, Task Queue, and HTTP triggers

These triggers are direct invocations. Because the 2nd gen function will have a new name (and a new URL for HTTP triggers), you can migrate traffic by updating the clients.

  1. Rename the function in your code (for example, rename myCallable to myCallableV2 ).
  2. Deploy the function. Both the 1st gen and 2nd gen functions are now running.
  3. Update your client code or caller to point to the new 2nd gen function's name or URL.
  4. Once all traffic has shifted to the new function, delete the 1st gen function using the Firebase CLI's firebase functions:delete command.

Migrate background triggers

Background triggers (such as Pub/Sub , Cloud Firestore , and Cloud Storage triggers) respond to events in your project. To avoid missing any events during the transition, you must temporarily run both the 1st gen and 2nd gen functions side-by-side.

During the transition period, both functions will trigger on the same event. This means your business logic will run twice per event. Ensure your function is idempotent before proceeding.

  1. Add the 2nd gen function alongside the 1st gen function, keeping the existing 1st gen function in your code, and adding the 2nd gen function listening to the same event source.

      import 
      
     * 
      
     as 
      
     functions 
      
     from 
      
     "firebase-functions/v1" 
     ; 
     import 
      
     { 
      
     onMessagePublished 
      
     } 
      
     from 
      
     "firebase-functions/v2/pubsub" 
     ; 
     // --- Existing 1st gen function --- 
     export 
      
     const 
      
     myPubSub 
      
     = 
      
     functions 
     . 
     pubsub 
     . 
     topic 
     ( 
     "my-topic" 
     ). 
     onPublish 
     (( 
     message 
     , 
      
     context 
     ) 
      
     = 
    >  
     { 
      
     console 
     . 
     log 
     ( 
     "V1 handler running for event:" 
     , 
      
     context 
     . 
     eventId 
     ); 
      
     // ... existing v1 function logic ... 
     }); 
     // --- New v2 passthrough function --- 
     export 
      
     const 
      
     myPubSubV2 
      
     = 
      
     onMessagePublished 
     ( 
     "my-topic" 
     , 
      
     async 
      
     ({ 
      
     message 
     , 
      
     context 
      
     }) 
      
     = 
    >  
     { 
      
     console 
     . 
     log 
     ( 
     "v2 handler triggering V1 for event:" 
     , 
      
     context 
     . 
     eventId 
     ); 
      
     // Call the v1 function's handler 
      
     await 
      
     myPubSub 
     . 
     run 
     ( 
     message 
     , 
      
     context 
     ); 
     }); 
     
    
  2. Run firebase deploy . Both functions are now active and listening to the same events.

  3. Verify the 2nd gen function is receiving traffic. Monitor the logs for both functions. Ensure that the 2nd gen function is invoked for all events and that calls are succeeding.

  4. Once you're confident that the function is performing correctly, move the actual business logic from the 1st gen function into the 2nd gen function's body. If you used the passthrough method, remove the call to myPubSub.run() .

      import 
      
     * 
      
     as 
      
     functions 
      
     from 
      
     "firebase-functions/v1" 
     ; 
     import 
      
     { 
      
     onMessagePublished 
      
     } 
      
     from 
      
     "firebase-functions/v2/pubsub" 
     ; 
     // --- Existing v1 function (to be removed next) --- 
     export 
      
     const 
      
     myPubSub 
      
     = 
      
     functions 
     . 
     pubsub 
     . 
     topic 
     ( 
     "my-topic" 
     ). 
     onPublish 
     (( 
     message 
     , 
      
     context 
     ) 
      
     = 
    >  
     { 
      
     console 
     . 
     log 
     ( 
     "v1 handler running for event:" 
     , 
      
     context 
     . 
     eventId 
     ); 
      
     // ... existing v1 function logic ... 
     }); 
     // --- New v2 function with full logic --- 
     export 
      
     const 
      
     myPubSubV2 
      
     = 
      
     onMessagePublished 
     ( 
     "my-topic" 
     , 
      
     ({ 
      
     message 
     , 
      
     context 
      
     }) 
      
     = 
    >  
     { 
      
     console 
     . 
     log 
     ( 
     "v2 handler running for event:" 
     , 
      
     context 
     . 
     eventId 
     ); 
      
     // ... existing v1 function logic WAS MOVED HERE ... 
     }); 
     
    

    Deploy this change.

  5. Remove the 1st gen function definition from your code and redeploy. The CLI will prompt you to delete the 1st gen function from Google Cloud .

Create a Mobile Website
View Site in Mobile | Classic
Share by: