Mengonfigurasi lingkungan Anda


Sering kali fungsi Anda memerlukan konfigurasi tambahan, seperti kunci API pihak ketiga atau setelan yang dapat disesuaikan. Firebase SDK untuk Cloud Functions menawarkan konfigurasi lingkungan bawaan untuk memudahkan penyimpanan dan pengambilan jenis data ini untuk project Anda.

Anda dapat memilih di antara tiga opsi:

  • Konfigurasi berparameter (direkomendasikan untuk sebagian besar skenario). Opsi ini menyediakan konfigurasi lingkungan strongly-typed dengan parameter yang divalidasi pada waktu deployment, sehingga dapat mencegah error dan menyederhanakan proses debug.
  • Konfigurasi berbasis file dari variabel lingkungan. Dengan pendekatan ini, Anda akan membuat file dotenv secara manual untuk memuat variabel lingkungan.
  • Konfigurasi lingkungan runtime dengan Firebase CLI dan functions.config (khusus Cloud Functions (generasi ke-1)).

Untuk sebagian besar kasus penggunaan, konfigurasi berparameter direkomendasikan. Dengan pendekatan ini, nilai konfigurasi tersedia baik saat runtime maupun deployment, dan deployment akan diblokir kecuali jika semua parameter memiliki nilai yang valid. Sebaliknya, konfigurasi dengan variabel lingkungan tidak tersedia pada waktu deployment.

Konfigurasi berparameter

Cloud Functions for Firebase menyediakan antarmuka untuk menetapkan parameter konfigurasi secara deklaratif di dalam codebase Anda. Nilai parameter ini tersedia selama deployment fungsi, saat menetapkan opsi deployment dan runtime, serta selama eksekusi. Hal ini berarti bahwa CLI akan memblokir deployment kecuali jika semua parameter memiliki nilai yang valid.

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

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!')

Saat men-deploy fungsi dengan variabel konfigurasi berparameter, Firebase CLI terlebih dahulu akan mencoba memuat nilai variabel tersebut dari file .env lokal. Jika nilai tersebut tidak ditemukan dan tidak ada default yang ditetapkan, CLI akan meminta nilai ini selama deployment, lalu otomatis menyimpannya ke file .env bernama .env.<project_ID> di direktori functions/ Anda:

$ 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

Bergantung pada alur kerja pengembangan Anda, sebaiknya tambahkan file .env.<project_ID> yang dihasilkan ke kontrol versi.

Mengonfigurasi perilaku CLI

Parameter dapat dikonfigurasi dengan objek Options yang mengontrol cara CLI meminta nilai. Contoh berikut menetapkan opsi untuk memvalidasi format nomor telepon, memberikan opsi pemilihan sederhana, dan mengisi opsi pemilihan secara otomatis dari project 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: {select: {options:
[{value: "odd"}, {value: "even"}]}}})

const storageBucket = defineString('BUCKET', {input: {resource: {type:
"storage.googleapis.com/Bucket"}}, description: "This will automatically
populate the selector field with the deploying Cloud Project’s
storage buckets"})

Python (pratinjau)

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

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="storage.googleapis.com/Bucket"),
    description="This will automatically populate the selector field with the deploying Cloud Project's storage buckets",
)

Jenis-jenis parameter

Konfigurasi berparameter menyediakan strong typing untuk parameter value, dan juga mendukung secret dari Cloud Secret Manager. Jenis yang didukung adalah:

  • Secret
  • String
  • Boolean
  • Bilangan bulat
  • Float

Nilai dan ekspresi parameter

Firebase mengevaluasi parameter pada waktu deployment dan saat fungsi dieksekusi. Karena adanya lingkungan ganda ini, Anda harus cermat saat membandingkan parameter value, dan saat menggunakannya untuk menetapkan opsi runtime bagi fungsi Anda.

Untuk meneruskan parameter ke fungsi Anda sebagai opsi runtime, teruskan secara langsung:

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

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):
    ...

Selain itu, jika Anda perlu membandingkan parameter untuk mengetahui opsi yang sebaiknya dipilih, Anda harus menggunakan komparator bawaan, bukan memeriksa nilai:

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

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):
    ...

Parameter dan ekspresi parameter yang hanya digunakan saat runtime dapat diakses dengan fungsi value-nya:

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

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!')

Parameter bawaan

Cloud Functions SDK menawarkan tiga parameter yang telah ditetapkan, yang tersedia dari sub-paket firebase-functions/params:

Node.js

  • projectId — project Cloud tempat fungsi dijalankan.
  • databaseUrl — URL instance Realtime Database yang terkait dengan fungsi tersebut (jika diaktifkan di project Firebase).
  • storageBucket — bucket Cloud Storage yang terkait dengan fungsi tersebut (jika diaktifkan di project Firebase).

Python (pratinjau)

  • PROJECT_ID — project Cloud tempat fungsi dijalankan.
  • DATABASE_URL — URL instance Realtime Database yang terkait dengan fungsi tersebut (jika diaktifkan di project Firebase).
  • STORAGE_BUCKET — bucket Cloud Storage yang terkait dengan fungsi tersebut (jika diaktifkan di project Firebase).

Dalam semua hal, parameter ini berfungsi seperti parameter string yang ditentukan pengguna. Namun, karena nilainya selalu diketahui oleh Firebase CLI, nilai tersebut tidak akan pernah diminta saat deployment atau disimpan ke file .env.

Parameter secret

Parameter jenis Secret, yang ditentukan menggunakan defineSecret(), mewakili parameter string yang memiliki nilai yang disimpan di Cloud Secret Manager. Alih-alih memeriksa file .env lokal dan menulis nilai baru ke file ini jika nilai tersebut tidak ada, parameter secret memeriksa keberadaan nilai di Cloud Secret Manager, dan secara interaktif meminta nilai secret baru selama deployment.

Parameter secret yang ditentukan dengan cara ini harus terikat dengan fungsi individual yang harus memiliki akses ke parameter tersebut:

Node.js

const { onRequest } = require("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 (pratinjau)

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

Karena nilai secret ini tersembunyi hingga fungsi dieksekusi, Anda tidak dapat menggunakannya saat mengonfigurasi fungsi.

Variabel lingkungan

Cloud Functions for Firebase mendukung format file dotenv untuk memuat variabel lingkungan yang ditentukan dalam .env file ke runtime aplikasi Anda. Setelah di-deploy, variabel lingkungan dapat dibaca melalui process.env antarmuka.

Untuk mengonfigurasi lingkungan dengan cara ini, buat file .env di project Anda, tambahkan variabel yang diinginkan, dan deploy:

  1. Buat file .env di direktori functions/ Anda:

    # Directory layout:
    #   my-project/
    #     firebase.json
    #     functions/
    #       .env
    #       package.json
    #       index.js
    
  2. Buka file .env untuk mengedit, lalu tambahkan kunci yang diinginkan. Contoh:

    PLANET=Earth
    AUDIENCE=Humans
    
  3. Deploy fungsi dan pastikan variabel lingkungan telah dimuat:

    firebase deploy --only functions
    # ...
    # i functions: Loaded environment variables from .env.
    # ...
    

Setelah variabel lingkungan kustom di-deploy, kode fungsi Anda dapat mengaksesnya dengan sintaksis process.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 (pratinjau)

import os

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

Men-deploy beberapa kumpulan variabel lingkungan

Jika Anda memerlukan kumpulan variabel lingkungan alternatif untuk project Firebase (seperti staging vs. produksi), buat file .env.<project or alias> dan tulis variabel lingkungan khusus project di sana. Variabel lingkungan dari .env dan file .env khusus project (jika ada) akan disertakan dalam semua fungsi yang di-deploy.

Misalnya, project dapat menyertakan tiga file ini yang berisi nilai yang sedikit berbeda untuk pengembangan dan produksi:

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

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Prod Humans

Mengingat nilai dalam file terpisah tersebut, kumpulan variabel lingkungan yang di-deploy dengan fungsi Anda akan bervariasi, bergantung pada project target:

$ 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

Variabel lingkungan yang dicadangkan

Beberapa kunci variabel lingkungan dicadangkan untuk penggunaan internal. Jangan gunakan kunci berikut dalam file .env Anda:

  • Semua kunci yang dimulai dengan X_GOOGLE_
  • Semua kunci yang dimulai dengan EXT_
  • Semua kunci yang dimulai dengan FIREBASE_
  • Kunci apa pun dari daftar berikut:
  • CLOUD_RUNTIME_CONFIG
  • ENTRY_POINT
  • GCP_PROJECT
  • GCLOUD_PROJECT
  • GOOGLE_CLOUD_PROJECT
  • FUNCTION_TRIGGER_TYPE
  • FUNCTION_NAME
  • FUNCTION_MEMORY_MB
  • FUNCTION_TIMEOUT_SEC
  • FUNCTION_IDENTITY
  • FUNCTION_REGION
  • FUNCTION_TARGET
  • FUNCTION_SIGNATURE_TYPE
  • K_SERVICE
  • K_REVISION
  • PORT
  • K_CONFIGURATION

Menyimpan dan mengakses informasi konfigurasi yang sensitif

Variabel lingkungan yang disimpan dalam file .env dapat digunakan untuk konfigurasi fungsi, tetapi tidak boleh dianggap sebagai cara yang aman untuk menyimpan informasi sensitif seperti kredensial database atau kunci API. Hal ini penting jika Anda melakukan check in file .env ke dalam kontrol sumber.

Untuk membantu Anda menyimpan informasi konfigurasi yang sensitif, Cloud Functions for Firebase terintegrasi dengan Google Cloud Secret Manager. Layanan terenkripsi ini menyimpan nilai konfigurasi dengan aman, sekaligus mengizinkan akses mudah dari fungsi Anda saat diperlukan.

Membuat dan menggunakan secret

Untuk membuat secret, gunakan Firebase CLI.

Untuk membuat dan menggunakan secret:

  1. Dari root direktori project lokal, jalankan perintah berikut:

    firebase functions:secrets:set SECRET_NAME

  2. Masukkan nilai untuk SECRET_NAME.

    CLI akan menampilkan pesan berhasil dan memperingatkan bahwa Anda harus men-deploy fungsi agar perubahan diterapkan.

  3. Sebelum men-deploy, pastikan kode fungsi Anda mengizinkan fungsi tersebut mengakses secret menggunakan parameter runWith:

    Node.js

    const { onRequest } = require("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 (pratinjau)

    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. Deploy Cloud Functions:

    firebase deploy --only functions

    Sekarang Anda dapat mengaksesnya seperti variabel lingkungan lainnya. Sebaliknya, jika fungsi lain yang tidak menentukan secret dalam runWith mencoba mengakses secret, fungsi tersebut akan menerima nilai yang tidak ditentukan:

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

    @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.
    

Setelah di-deploy, fungsi tersebut akan memiliki akses ke nilai secret. Hanya fungsi yang secara khusus menyertakan secret dalam parameter runWith-nya yang akan memiliki akses ke secret tersebut sebagai variabel lingkungan. Cara ini membantu Anda memastikan bahwa nilai secret hanya tersedia di tempat yang diperlukan, sehingga mengurangi risiko kebocoran secret secara tidak sengaja.

Mengelola secret

Gunakan Firebase CLI untuk mengelola secret Anda. Saat mengelola secret dengan cara ini, perlu diingat bahwa beberapa perubahan CLI mengharuskan Anda untuk mengubah dan/atau men-deploy ulang fungsi-fungsi terkait. Secara khusus:

  • Setiap kali Anda menetapkan nilai baru untuk secret, Anda harus men-deploy ulang semua fungsi yang merujuk ke secret tersebut agar fungsi dapat mengambil nilai terbaru.
  • Jika Anda menghapus secret, pastikan tidak ada satu pun fungsi yang di-deploy merujuk ke secret tersebut. Fungsi yang menggunakan nilai secret yang telah dihapus akan gagal tanpa ada peringatan.

Berikut adalah ringkasan perintah Firebase CLI untuk pengelolaan secret:

# 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

Untuk perintah access dan destroy, Anda dapat memberikan parameter versi opsional untuk mengelola versi tertentu. Contoh:

functions:secrets:access SECRET_NAME[@VERSION]

Untuk mengetahui informasi selengkapnya tentang operasi ini, teruskan -h dengan perintah untuk melihat bantuan CLI.

Cara penagihan secret

Secret Manager mengizinkan 6 versi secret aktif tanpa biaya. Artinya, Anda dapat memiliki 6 secret per bulan di project Firebase tanpa biaya.

Secara default, Firebase CLI berupaya secara otomatis menghancurkan versi secret yang tidak digunakan jika perlu, seperti ketika Anda men-deploy fungsi dengan versi secret baru. Selain itu, Anda dapat secara aktif membersihkan secret yang tidak digunakan menggunakan functions:secrets:destroy dan functions:secrets:prune.

Secret Manager mengizinkan 10.000 operasi akses bulanan yang tidak ditagih untuk sebuah secret. Instance fungsi hanya membaca secret yang ditentukan dalam parameter runWith-nya setiap kali cold start dilakukan padanya. Jika Anda memiliki banyak instance fungsi yang membaca banyak secret, project Anda dapat melebihi kuota ini, dan setelah itu Anda akan dikenai biaya $0,03 per 10.000 operasi akses.

Untuk mengetahui informasi selengkapnya, lihat Harga Secret Manager.

Dukungan emulator

Konfigurasi lingkungan dengan dotenv dirancang agar dapat saling beroperasi dengan emulator Cloud Functions lokal.

Saat menggunakan emulator Cloud Functions lokal, Anda dapat mengganti variabel lingkungan untuk project dengan menyiapkan file .env.local. Konten .env.local lebih diutamakan daripada .env dan file .env khusus project.

Misalnya, project dapat menyertakan tiga file ini yang berisi nilai yang sedikit berbeda untuk pengembangan dan pengujian lokal:

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

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Local Humans

Saat dimulai dalam konteks lokal, emulator akan memuat variabel lingkungan seperti yang ditunjukkan berikut ini:

  $ firebase emulators:start
  i  emulators: Starting emulators: functions
  # Starts emulator with following environment variables:
  #  PLANET=Earth
  #  AUDIENCE=Local Humans

Secret dan kredensial di emulator Cloud Functions

Emulator Cloud Functions mendukung penggunaan secret untuk menyimpan dan mengakses informasi konfigurasi yang sensitif. Secara default, emulator akan mencoba mengakses secret production Anda menggunakan kredensial default aplikasi. Dalam situasi tertentu seperti lingkungan CI, emulator mungkin gagal mengakses nilai secret karena pembatasan izin.

Serupa dengan dukungan emulator Cloud Functions untuk variabel lingkungan, Anda dapat mengganti nilai secret dengan menyiapkan file .secret.local. Hal ini memudahkan Anda untuk menguji fungsi secara lokal, terutama jika tidak memiliki akses ke nilai secret.

Bermigrasi dari konfigurasi lingkungan

Jika menggunakan konfigurasi lingkungan dengan functions.config, Anda dapat memigrasikan konfigurasi yang ada sebagai variabel lingkungan (dalam format dotenv). Firebase CLI menyediakan perintah ekspor yang menghasilkan output konfigurasi setiap alias atau project yang tercantum dalam file .firebaserc direktori Anda (dalam contoh di bawah, local, dev, dan prod) sebagai file .env.

Untuk melakukan migrasi, ekspor konfigurasi lingkungan yang ada menggunakan perintah 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

Perhatikan bahwa, dalam beberapa kasus, Anda akan diminta untuk memasukkan awalan guna mengganti nama kunci variabel lingkungan yang diekspor. Ini karena tidak semua konfigurasi dapat diubah secara otomatis karena mungkin tidak valid atau merupakan kunci variabel lingkungan yang dicadangkan.

Sebaiknya tinjau dengan cermat konten file .env yang dihasilkan sebelum men-deploy fungsi atau lakukan check in file .env ke dalam kontrol sumber. Jika ada nilai yang sensitif dan tidak boleh bocor, hapus nilai tersebut dari file .env Anda dan simpan dengan aman di Secret Manager.

Anda juga harus memperbarui kode fungsi. Setiap fungsi yang menggunakan functions.config sekarang harus menggunakan process.env, seperti yang ditunjukkan dalam Variabel lingkungan.