1. nesil Node.js işlevlerini 2. nesle yükseltme

1. nesil işlevleri kullanan uygulamalar, bu kılavuzdaki talimatları uygulayarak 2. nesil işlevlere geçmeyi düşünebilir. 2. nesil işlevler, daha iyi performans, daha iyi yapılandırma, daha iyi izleme ve daha fazlasını sağlamak için Cloud Run'ı kullanır.

Bu belgedeki örneklerde, CommonJS modülleriyle (require stilinde içe aktarmalar) JavaScript kullandığınız varsayılır. Ancak aynı ilkeler, ESM (import … from stilinde içe aktarmalar) ile JavaScript ve TypeScript için de geçerlidir.

Taşıma süreci

1. nesil ve 2. nesil işlevler aynı dosyada yan yana bulunabilir. Bu sayede, hazır olduğunuzda kod tabanınızı parça parça taşıyabilirsiniz. Devam etmeden önce her seferinde bir işlevi taşımanızı, test ve doğrulama yapmanızı öneririz.

Firebase KSA ve firebase-function sürümlerini doğrulama

En az Firebase KSA sürümü 12.00 ve firebase-functions sürümü 4.3.0 kullandığınızdan emin olun. Daha yeni sürümler hem 2. nesli hem de 1. nesli destekler.

İçe aktarma işlemlerini güncelleme

2. nesil işlevler, firebase-functions SDK'sındaki v2 alt paketinden içe aktarılır. Bu farklı içe aktarma yolu, Firebase CLI'nın işlev kodunuzu 1. veya 2. nesil işlev olarak dağıtıp dağıtmayacağını belirlemek için ihtiyaç duyduğu tek şeydir.

v2 alt paketi modülerdir ve yalnızca ihtiyacınız olan belirli modülü içe aktarmanızı öneririz.

Önce: 1. nesil

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

Sonra: 2. nesil

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

Tetikleyici tanımlarını güncelleme

2. nesil SDK, modüler içe aktarmaları tercih ettiğinden tetikleyici tanımlarını, önceki adımdaki değişen içe aktarmaları yansıtacak şekilde güncelleyin.

Bazı tetikleyiciler için geri çağırmalara iletilen bağımsız değişkenler değişti. Bu örnekte, onDocumentCreated geri çağırmasının bağımsız değişkenlerinin tek bir event nesnesinde birleştirildiğini unutmayın. Ayrıca, bazı tetikleyicilerde onRequest tetikleyicisinin cors seçeneği gibi kullanışlı yeni yapılandırma özellikleri bulunur.

Önce: 1. nesil

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

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

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

Sonra: 2. nesil

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

JavaScript ile yapı bozma sayesinde yeniden yazma çabalarını en aza indirme

İşlevleriniz, 1. nesil bağlam veya sağlayıcıya özel parametreler (ör. message veya snapshot) kullanan karmaşık gövdelere sahipse 2. nesil SDK'ya yerleştirilmiş 1. nesil uyumluluk yardımcılarını kullanabilirsiniz.

2. nesil SDK, etkinlik nesnesini 1. nesil imzalarıyla eşleşen alıcılarla otomatik olarak yamalar. Bu sayede, işlev mantığınızı yeniden yazma ihtiyacını en aza indirerek bu özellikleri doğrudan işleyici imzasında ayıklamak için JavaScript destructuring'i kullanabilirsiniz.

Sağlayıcı eşleme referansı

Sağlayıcı 1. nesil bağımsız değişkenler 2. nesil düzeltilmiş etkinlik yapısını bozma
Pub/Sub (message, context) ({ message, context }) => { ... }
Firestore (snapshot, context) ({ snapshot, context }) => { ... }
Depolama (object, context) ({ object, context }) => { ... }
Realtime Database (snapshot, context) ({ snapshot, context }) => { ... }
Remote Config (version, context) ({ version, context }) => { ... }
Planlayıcı (context) ({ context }) => { ... }
Görev Sırası (data, context) ({ data, context }) => { ... }

Önce (1. nesil):

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

Yeni Alternatif (2. nesil, Destructuring ile):

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

Parametre haline getirilmiş yapılandırmayı kullanma

2. nesil işlevler, kod tabanınızda yapılandırma parametrelerini bildirimsel olarak tanımlamak için daha güvenli bir arayüz sunmak amacıyla functions.config desteğini bırakıyor. Yeni params modülüyle, tüm parametreler geçerli bir değere sahip olmadığı sürece CLI dağıtımı engeller. Böylece, bir işlevin eksik yapılandırmayla dağıtılmaması sağlanır.

Önce: 1. nesil

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

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

Sonra: 2. nesil

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

functions.config ile mevcut bir ortam yapılandırmanız varsa 2. nesle yükseltme işleminizin bir parçası olarak bu yapılandırmayı taşıyın.

functions.config API'sinin desteği sonlandırıldı ve Mart 2027'de kullanımdan kaldırılacak. Bu tarihten sonra, functions.config ile yapılan dağıtımlar başarısız olacak.

Dağıtım hatalarını önlemek için yapılandırmanızı Firebase KSA'yı kullanarak Cloud Secret Manager'a taşıyın. Bu, yapılandırmanızı taşımanın en verimli ve güvenli yolu olduğundan önemle tavsiye edilir.

  1. Firebase CLI ile yapılandırmayı dışa aktarma

    Mevcut ortam yapılandırmanızı Cloud Secret Manager'da yeni bir gizli anahtara aktarmak için config export komutunu kullanın:

    $ 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. Gizli bilgileri bağlamak için işlev kodunu güncelleme

    Cloud Secret Manager'daki yeni gizli dizide depolanan yapılandırmayı kullanmak için işlev kaynağınızda defineJsonSecret API'sini kullanın. Ayrıca, gizli anahtarların ihtiyaç duyulan tüm işlevlere bağlı olduğundan emin olun.

    Önce

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

    Sonra

    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. İşlevleri dağıtma

    Değişiklikleri uygulamak ve gizli izinleri bağlamak için güncellenen işlevlerinizi dağıtın.

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

Çalışma zamanı seçeneklerini ayarlama

Çalışma zamanı seçeneklerinin yapılandırılması 1. ve 2. nesil arasında değişti. 2. nesilde ayrıca tüm işlevler için seçenekleri ayarlama özelliği de eklendi.

Önce: 1. nesil

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

Sonra: 2. nesil

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

Varsayılan hizmet hesabını güncelleme (isteğe bağlı)

1. nesil işlevler, Firebase API'lerine erişimi yetkilendirmek için Google App Engine varsayılan hizmet hesabını kullanırken 2. nesil işlevler Compute Engine varsayılan hizmet hesabını kullanır. Bu fark, 1. nesil hizmet hesabına özel izinler verdiğiniz durumlarda 2. nesil sürüme taşınan işlevlerle ilgili izin sorunlarına yol açabilir. Herhangi bir hizmet hesabı iznini değiştirmediyseniz bu adımı atlayabilirsiniz.

Önerilen çözüm, mevcut 1. nesil App Engine varsayılan hizmet hesabını 2. nesile taşımak istediğiniz işlevlere açıkça atayarak 2. nesil varsayılanını geçersiz kılmaktır. Bunu, taşınan her işlevin serviceAccountEmail için doğru değeri ayarladığından emin olarak yapabilirsiniz:

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

Alternatif olarak, hizmet hesabı ayrıntılarını hem App Engine varsayılan hizmet hesabındaki (1. nesil için) hem de Compute Engine varsayılan hizmet hesabındaki (2. nesil için) gerekli tüm izinlerle eşleşecek şekilde değiştirebilirsiniz.

Eşzamanlılığı kullanma

2. nesil işlevlerin önemli bir avantajı, tek bir işlev örneğinin aynı anda birden fazla isteğe hizmet verebilmesidir. Bu, son kullanıcıların yaşadığı yeni başlatma sayısını önemli ölçüde azaltabilir. Eşzamanlılık varsayılan olarak 80'e ayarlanır ancak 1 ila 1.000 arasında herhangi bir değere ayarlayabilirsiniz:

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

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

Eşzamanlılığı ayarlamak performansı artırabilir ve işlevlerin maliyetini düşürebilir. Eşzamanlı isteklere izin ver bölümünde eşzamanlılık hakkında daha fazla bilgi edinin.

Global değişken kullanımını denetleme

Eşzamanlılık göz önünde bulundurulmadan yazılan 1. nesil işlevler, her istekte ayarlanan ve okunan global değişkenler kullanabilir. Eşzamanlılık etkinleştirildiğinde ve tek bir örnek aynı anda birden fazla isteği işlemeye başladığında, eşzamanlı istekler genel değişkenleri aynı anda ayarlamaya ve okumaya başladığından işlevinizde hatalar oluşabilir.

Yükseltme sırasında işlevinizin CPU'sunu gcf_gen1 olarak ayarlayabilir ve 1. nesil davranışını geri yüklemek için concurrency değerini 1 olarak ayarlayabilirsiniz:

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

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

Ancak bu, 2. nesil işlevlerin performans avantajlarını ortadan kaldırdığı için uzun vadeli bir çözüm olarak önerilmez. Bunun yerine, işlevlerinizdeki global değişkenlerin kullanımını denetleyin ve hazır olduğunuzda bu geçici ayarları kaldırın.

Trafiği yeni 2. nesil işlevlere taşıma

Bir işlevin bölgesini veya tetikleyici türünü değiştirirken olduğu gibi, 2. nesil işleve yeni bir ad vermeniz ve trafiği yavaş yavaş bu işleve geçirmeniz gerekir.

Aynı ada sahip bir işlevi 1. nesilden 2. nesile yükseltmek ve firebase deploy çalıştırmak mümkün değildir. Bu durumda şu hata mesajı gösterilir:

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

Taşıma stratejisi, işlevinizin kullandığı tetikleyici türüne bağlıdır.

Callable, Task Queue ve HTTP tetikleyicileri için

Bu tetikleyiciler doğrudan çağırmalardır. 2. nesil işlevin yeni bir adı (ve HTTP tetikleyicileri için yeni bir URL) olacağından, istemcileri güncelleyerek trafiği taşıyabilirsiniz.

  1. Kodunuzdaki işlevi yeniden adlandırın (ör. myCallable işlevini myCallableV2 olarak yeniden adlandırın).
  2. İşlevi dağıtın. Hem 1. nesil hem de 2. nesil işlevler artık çalışıyor.
  3. İstemci kodunuzu veya arayanınızı, yeni 2. nesil işlevin adını ya da URL'sini işaret edecek şekilde güncelleyin.
  4. Tüm trafik yeni işleve yönlendirildikten sonra Firebase CLI'nin firebase functions:delete komutunu kullanarak 1. nesil işlevi silin.

Arka plan tetikleyicileri için (Pub/Sub, Cloud Firestore, Cloud Storage vb.)

Arka plan tetikleyicileri, projenizdeki etkinliklere yanıt verir. Geçiş sırasında etkinlikleri kaçırmamak için hem 1. nesil hem de 2. nesil işlevleri geçici olarak yan yana çalıştırmanız gerekir.

Geçiş döneminde her iki işlev de aynı etkinlikte tetiklenir. Bu durumda, işletme mantığınız her etkinlik için iki kez çalışır. Devam etmeden önce işlevinizin idempotent olduğundan emin olun.

1. adım: 2. nesil işlevi 1. nesil işlevin yanına ekleyin

Mevcut 1. nesil işlevinizi kodunuzda tutun ve aynı etkinlik kaynağını dinleyen 2. nesil işlevi ekleyin.

Profesyonel İpucu: Doğrulama için geçiş kullanın Geçiş sırasında işletme mantığınızın kod tabanınızda yinelenmesini önlemek veya 2. nesil işlevin olayları tam olarak güvenmeden önce doğru şekilde aldığını doğrulamak için 2. nesil işlevi, run yöntemini kullanarak 1. nesil işlevi çağıran bir geçiş işlevi haline getirin.

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. adım: Her iki işlevi de dağıtın

firebase deploy komutunu çalıştırın. Her iki işlev de artık etkin ve aynı etkinlikleri dinliyor.

3. adım: 2. nesil işlevin trafik aldığını doğrulayın

Her iki işlevin günlüklerini izleyin. Tüm etkinlikler için 2. nesil işlevin çağrıldığından ve çağrıların başarılı olduğundan emin olun.

4. adım: Tam mantığı 2. nesil işlevine taşıyın

İşlemin doğru yapıldığından emin olduğunuzda, asıl iş mantığını 1. nesil işlevden 2. nesil işlevin gövdesine taşıyın. Geçiş yöntemini kullandıysanız myPubSub.run() çağrısını kaldırın.

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

Bu değişikliği dağıtın.

5. adım: 1. nesil işlevi devre dışı bırakın

1. nesil işlev tanımını kodunuzdan kaldırın ve yeniden dağıtın. KSA, 1. nesil işlevi Google Cloud'dan silmenizi ister.

import { onMessagePublished } from "firebase-functions/v2/pubsub";

// --- V1 function definition REMOVED ---

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