กำหนดค่าสภาพแวดล้อม

บ่อยครั้งที่คุณจะต้องกำหนดค่าเพิ่มเติมสำหรับฟังก์ชัน เช่น คีย์ API ของบุคคลที่สามหรือการตั้งค่าที่ปรับแต่งได้ Firebase SDK สำหรับ Cloud Functions มีการกำหนดค่าสภาพแวดล้อมในตัวเพื่อให้จัดเก็บและเรียกข้อมูลประเภทนี้สำหรับโปรเจ็กต์ได้ง่าย

โดยเลือกได้ดังนี้

  • การกำหนดค่าแบบกำหนดพารามิเตอร์ (แนะนำสำหรับสถานการณ์ส่วนใหญ่) ซึ่งจะช่วยให้กำหนดค่าสภาพแวดล้อมที่มีประเภทชัดเจนด้วยพารามิเตอร์ที่ได้รับการตรวจสอบ ณ เวลาที่ติดตั้งใช้งาน ซึ่งจะช่วยป้องกันข้อผิดพลาดและทำให้การแก้ไขข้อบกพร่องง่ายขึ้น
  • การกำหนดค่าตัวแปรสภาพแวดล้อมตามไฟล์ วิธีนี้คือการสร้างไฟล์ dotenv ด้วยตนเองเพื่อโหลดตัวแปรสภาพแวดล้อม

สําหรับ Use Case ส่วนใหญ่ เราขอแนะนําให้ใช้การกําหนดค่าที่มีพารามิเตอร์ วิธีนี้ทำให้ค่าการกำหนดค่าพร้อมใช้งานทั้งในรันไทม์และเวลาติดตั้งใช้งาน และระบบจะบล็อกการติดตั้งใช้งานเว้นแต่พารามิเตอร์ทั้งหมดจะมีค่าที่ถูกต้อง ในทางกลับกัน การกำหนดค่าด้วยตัวแปรสภาพแวดล้อมจะไม่พร้อมใช้งานในเวลาที่ทำให้ใช้งานได้

การกำหนดค่าที่มีพารามิเตอร์

Cloud Functions for Firebase มีอินเทอร์เฟซสำหรับกำหนดพารามิเตอร์การกำหนดค่าแบบประกาศภายในฐานของโค้ด ค่าของพารามิเตอร์เหล่านี้จะใช้ได้ทั้งในระหว่างการติดตั้งใช้งานฟังก์ชัน เมื่อตั้งค่าตัวเลือกการติดตั้งใช้งานและรันไทม์ และในระหว่างการดำเนินการ ซึ่งหมายความว่า CLI จะบล็อกการติดตั้งใช้งาน เว้นแต่พารามิเตอร์ทั้งหมดจะมีค่าที่ถูกต้อง

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

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

เมื่อติดตั้งใช้งานฟังก์ชันที่มีตัวแปรการกำหนดค่าที่กำหนดพารามิเตอร์ไว้ Firebase CLI จะพยายามโหลดค่าจากไฟล์ .env ในเครื่องก่อน หากไม่มีอยู่ในไฟล์เหล่านั้นและไม่ได้ตั้งค่า default ไว้ CLI จะแจ้งให้ป้อนค่าในระหว่างการติดตั้งใช้งาน แล้วบันทึกค่าเหล่านั้นลงในไฟล์ .env ที่ชื่อ .env.<project_ID> ในไดเรกทอรี functions/ โดยอัตโนมัติ

$ 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

การเพิ่มไฟล์ .env.<project_ID> ที่สร้างขึ้นลงในการควบคุมเวอร์ชันอาจเป็นประโยชน์ ทั้งนี้ขึ้นอยู่กับเวิร์กโฟลว์การพัฒนาของคุณ

การใช้พารามิเตอร์ในขอบเขตส่วนกลาง

ในระหว่างการติดตั้งใช้งาน ระบบจะโหลดและตรวจสอบโค้ดฟังก์ชันก่อนที่พารามิเตอร์จะมีค่าจริง ซึ่งหมายความว่าการดึงค่าพารามิเตอร์ระหว่างขอบเขตส่วนกลางจะส่งผลให้การติดตั้งใช้งานไม่สำเร็จ ในกรณีที่คุณต้องการใช้พารามิเตอร์เพื่อเริ่มต้นค่าส่วนกลาง ให้ใช้ Callback การเริ่มต้น onInit() Callback นี้จะทำงานก่อนที่ฟังก์ชันใดๆ จะทำงานในเวอร์ชันที่ใช้งานจริง แต่จะไม่ถูกเรียกใช้ในระหว่างการทำให้ใช้งานได้ จึงเป็นตำแหน่งที่ปลอดภัยในการเข้าถึงค่าของพารามิเตอร์

Node.js

const { GoogleGenerativeAI } = require('@google/generative-ai');
const { defineSecret } = require('firebase-functions/params');
const { onInit } = require('firebase-functions/v2/core');

const apiKey = defineSecret('GOOGLE_API_KEY');

let genAI;
onInit(() => {
  genAI = new GoogleGenerativeAI(apiKey.value());
})

Python

from firebase_functions.core import init
from firebase_functions.params import StringParam, PROJECT_ID
import firebase_admin
import vertexai

location = StringParam("LOCATION")

x = "hello"

@init
def initialize():
  # Note: to write back to a global, you'll need to use the "global" keyword
  # to avoid creating a new local with the same name.
  global x
  x = "world"
  firebase_admin.initialize_app()
  vertexai.init(PROJECT_ID.value, location.value)

หากคุณใช้พารามิเตอร์ประเภท Secret โปรดทราบว่าพารามิเตอร์ดังกล่าวจะใช้ได้เฉพาะในกระบวนการของฟังก์ชันที่เชื่อมโยงกับข้อมูลลับเท่านั้น หากมีการเชื่อมโยงข้อมูลลับในบางฟังก์ชันเท่านั้น ให้ตรวจสอบว่า secret.value() เป็นเท็จหรือไม่ก่อนที่จะใช้

กำหนดค่าลักษณะการทำงานของ CLI

คุณสามารถกำหนดค่าพารามิเตอร์ด้วยออบเจ็กต์ Options ที่ควบคุมวิธีที่ CLI จะแจ้งให้ป้อนค่า ตัวอย่างต่อไปนี้จะตั้งค่าตัวเลือกเพื่อตรวจสอบรูปแบบของหมายเลขโทรศัพท์ เพื่อให้ตัวเลือกการเลือกที่เรียบง่าย และเพื่อสร้างตัวเลือกการเลือกโดยอัตโนมัติจากโปรเจ็กต์ 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: params.select(["odd", "even"])});

const memory = defineInt("MEMORY", {
  description: "How much memory do you need?",
  input: params.select({ "micro": 256, "chonky": 2048 }),
});

const extensions = defineList("EXTENSIONS", {
  description: "Which file types should be processed?",
  input: params.multiSelect(["jpg", "tiff", "png", "webp"]),
});

const storageBucket = defineString('BUCKET', {
  description: "This will automatically
populate the selector field with the deploying Cloud Project’s
storage buckets",
  input: params.PICK_STORAGE_BUCKET,
});

Python

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

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

ประเภทพารามิเตอร์

การกำหนดค่าที่มีพารามิเตอร์จะให้การพิมพ์ที่เข้มงวดสำหรับค่าพารามิเตอร์ และยังรองรับข้อมูลลับจาก Cloud Secret Manager ด้วย ประเภทที่รองรับมีดังนี้

  • ข้อมูลลับ
  • สตริง
  • บูลีน
  • จำนวนเต็ม
  • ทศนิยม
  • รายการ (Node.js)
  • JSON Secret (Node.js)

ดูข้อมูลเกี่ยวกับฟังก์ชันสำหรับการกำหนดพารามิเตอร์ได้ที่ข้อมูลอ้างอิงเนมสเปซ params

ค่าพารามิเตอร์และนิพจน์

Firebase จะประเมินพารามิเตอร์ทั้งในเวลาที่ติดตั้งใช้งานและขณะที่ฟังก์ชันทำงานอยู่ เนื่องจากสภาพแวดล้อมทั้ง 2 แบบนี้ คุณจึงต้องระมัดระวังเป็นพิเศษเมื่อเปรียบเทียบค่าพารามิเตอร์ และเมื่อใช้ค่าพารามิเตอร์เพื่อตั้งค่าตัวเลือกรันไทม์สำหรับฟังก์ชัน

หากต้องการส่งพารามิเตอร์ไปยังฟังก์ชันเป็นตัวเลือกรันไทม์ ให้ส่งโดยตรงดังนี้

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

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

นอกจากนี้ หากต้องการเปรียบเทียบกับพารามิเตอร์เพื่อดูว่าควรเลือกตัวเลือกใด คุณจะต้องใช้ตัวเปรียบเทียบในตัวแทนการตรวจสอบค่า ดังนี้

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

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

พารามิเตอร์และนิพจน์พารามิเตอร์ที่ใช้เฉพาะในรันไทม์จะเข้าถึงได้ด้วยฟังก์ชัน value ดังนี้

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

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

พารามิเตอร์ในตัว

Cloud Functions SDK มีพารามิเตอร์ที่กำหนดไว้ล่วงหน้า 3 รายการ ซึ่งพร้อมใช้งานจากแพ็กเกจย่อย firebase-functions/params ดังนี้

Node.js

  • projectID — โปรเจ็กต์ที่อยู่ในระบบคลาวด์ที่ฟังก์ชันทำงานอยู่
  • databaseURL — URL ของอินสแตนซ์ Realtime Database ที่เชื่อมโยงกับฟังก์ชัน (หากเปิดใช้ในโปรเจ็กต์ Firebase)
  • storageBucket — บัケット Cloud Storage ที่เชื่อมโยงกับฟังก์ชัน (หากเปิดใช้ในโปรเจ็กต์ Firebase)

Python

  • PROJECT_ID — โปรเจ็กต์ที่อยู่ในระบบคลาวด์ที่ฟังก์ชันทำงานอยู่
  • DATABASE_URL — URL ของอินสแตนซ์ Realtime Database ที่เชื่อมโยงกับฟังก์ชัน (หากเปิดใช้ในโปรเจ็กต์ Firebase)
  • STORAGE_BUCKET — บัケット Cloud Storage ที่เชื่อมโยงกับฟังก์ชัน (หากเปิดใช้ในโปรเจ็กต์ Firebase)

ฟังก์ชันเหล่านี้จะทำงานเหมือนกับพารามิเตอร์สตริงที่ผู้ใช้กำหนดในทุกๆ ด้าน ยกเว้นว่าเนื่องจาก Firebase CLI ทราบค่าของพารามิเตอร์เหล่านี้เสมอ ระบบจึงจะไม่แจ้งให้ป้อนค่าเมื่อติดตั้งใช้งาน และจะไม่บันทึกค่าลงในไฟล์ .env

พารามิเตอร์ข้อมูลลับ

พารามิเตอร์ประเภท Secret ซึ่งกำหนดโดยใช้ defineSecret() แสดงถึงพารามิเตอร์สตริงที่มีค่าจัดเก็บไว้ใน Cloud Secret Manager พารามิเตอร์ข้อมูลลับจะตรวจสอบการมีอยู่ของข้อมูลลับใน Cloud Secret Manager และแจ้งให้ป้อนค่าของข้อมูลลับใหม่แบบอินเทอร์แอกทีฟระหว่างการทำให้ใช้งานได้ แทนที่จะตรวจสอบกับไฟล์ .env ในเครื่องและเขียนค่าใหม่ลงในไฟล์หากไม่มี

พารามิเตอร์ข้อมูลลับต้องเชื่อมโยงกับฟังก์ชันแต่ละรายการที่ควรมีสิทธิ์เข้าถึงพารามิเตอร์ดังกล่าว

Node.js

const { onRequest } = require('firebase-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

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

เนื่องจากค่าของข้อมูลลับจะถูกซ่อนไว้จนกว่าจะมีการเรียกใช้ฟังก์ชัน คุณจึงไม่สามารถใช้ค่าดังกล่าวขณะกำหนดค่าฟังก์ชันได้

ข้อมูลลับ JSON ที่มีโครงสร้าง

หากคุณมีค่าการกำหนดค่าหลายค่าที่เกี่ยวข้องอย่างสมเหตุสมผล (เช่น การตั้งค่าสำหรับบริการของบุคคลที่สาม) คุณสามารถจัดเก็บค่าเหล่านั้นไว้ด้วยกันเป็นออบเจ็กต์ JSON ที่มีโครงสร้างภายในข้อมูลลับเดียวโดยใช้ defineJsonSecret() วิธีนี้จะช่วยจัดระเบียบการกำหนดค่าและใช้รุ่นฟรีของ Cloud Secret Manager ได้อย่างมีประสิทธิภาพมากขึ้นโดยการจัดเก็บกลุ่มค่าการกำหนดค่าที่เกี่ยวข้องไว้ในข้อมูลลับเดียว

ค่าที่จัดเก็บไว้ใน Secret Manager ต้องเป็นสตริง JSON ที่ถูกต้อง SDK จะแยกวิเคราะห์สตริง JSON เป็นออบเจ็กต์ JavaScript โดยอัตโนมัติเมื่อคุณเข้าถึง .value()

ตัวอย่าง

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

// Define a single secret to hold all configuration for some API
const someApiConfig = defineJsonSecret('SOMEAPI_CONFIG');

exports.myApi = onRequest(
  { secrets: [someApiConfig] },
  (req, res) => {
    // someApiConfig.value() automatically parses the JSON secret
    const { apiKey, webhookSecret, clientId } = someApiConfig.value();

    // Now you can use apiKey, webhookSecret, clientId
    // ...
  }
);

หากต้องการสร้างข้อมูลลับ SOMEAPI_CONFIG คุณจะต้องตั้งค่าใน Secret Manager เป็นสตริง JSON เช่น

{
  "apiKey": "key_...",
  "webhookSecret": "secret_...",
  "clientId": "client_..."
}

หากค่าของข้อมูลลับไม่ใช่ JSON ที่ถูกต้อง การเข้าถึง someApiConfig.value() จะทำให้เกิดข้อผิดพลาดที่รันไทม์

ตัวแปรสภาพแวดล้อม

Cloud Functions for Firebase รองรับรูปแบบไฟล์ dotenv สำหรับการโหลดตัวแปรสภาพแวดล้อมที่ระบุในไฟล์ .env ไปยังรันไทม์ของแอปพลิเคชัน เมื่อติดตั้งใช้งานแล้ว คุณจะอ่านตัวแปรสภาพแวดล้อมได้ผ่านอินเทอร์เฟซ process.env (ในโปรเจ็กต์ที่ใช้ Node.js) หรือ os.environ (ในโปรเจ็กต์ที่ใช้ Python)

หากต้องการกำหนดค่าสภาพแวดล้อมด้วยวิธีนี้ ให้สร้างไฟล์ .env ในโปรเจ็กต์ เพิ่มตัวแปรที่ต้องการ แล้วทำให้ใช้งานได้ ดังนี้

  1. สร้างไฟล์ .env ในไดเรกทอรี functions/ โดยทำดังนี้

    # Directory layout:
    #   my-project/
    #     firebase.json
    #     functions/
    #       .env
    #       package.json
    #       index.js
    
  2. เปิดไฟล์ .env เพื่อแก้ไข และเพิ่มคีย์ที่ต้องการ เช่น

    PLANET=Earth
    AUDIENCE=Humans
    
  3. ทำให้ฟังก์ชันใช้งานได้และยืนยันว่าโหลดตัวแปรสภาพแวดล้อมแล้ว โดยทำดังนี้

    firebase deploy --only functions
    # ...
    # i functions: Loaded environment variables from .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

import os

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

การติดตั้งใช้งานตัวแปรสภาพแวดล้อมหลายชุด

หากต้องการชุดตัวแปรสภาพแวดล้อมอื่นสำหรับโปรเจ็กต์ Firebase (เช่น สภาพแวดล้อมทดลองใช้เทียบกับสภาพแวดล้อมที่ใช้งานจริง) ให้สร้างไฟล์ .env.<project or alias> และเขียนตัวแปรสภาพแวดล้อมเฉพาะโปรเจ็กต์ไว้ที่นั่น ตัวแปรสภาพแวดล้อมจากไฟล์ .env และไฟล์ .env เฉพาะโปรเจ็กต์ (หากมี) จะรวมอยู่ในฟังก์ชันที่ติดตั้งใช้งานทั้งหมด

ตัวอย่างเช่น โปรเจ็กต์อาจมีไฟล์ 3 ไฟล์ต่อไปนี้ซึ่งมีค่าแตกต่างกันเล็กน้อยสำหรับการพัฒนาและการใช้งานจริง

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

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Prod Humans

เมื่อพิจารณาค่าในไฟล์แยกต่างหากเหล่านั้น ชุดตัวแปรสภาพแวดล้อมที่ติดตั้งใช้งานกับฟังก์ชันจะแตกต่างกันไปตามโปรเจ็กต์เป้าหมาย ดังนี้

$ 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

ตัวแปรสภาพแวดล้อมที่สงวนไว้

คีย์ตัวแปรสภาพแวดล้อมบางรายการสงวนไว้สำหรับการใช้งานภายใน อย่าใช้คีย์เหล่านี้ในไฟล์ .env

  • คีย์ทั้งหมดที่ขึ้นต้นด้วย X_GOOGLE_
  • คีย์ทั้งหมดที่ขึ้นต้นด้วย EXT_
  • คีย์ทั้งหมดที่ขึ้นต้นด้วย FIREBASE_
  • คีย์ใดก็ได้จากรายการต่อไปนี้
  • 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

จัดเก็บและเข้าถึงข้อมูลการกำหนดค่าที่ละเอียดอ่อน

ตัวแปรสภาพแวดล้อมที่จัดเก็บไว้ในไฟล์ .env สามารถใช้สำหรับการกำหนดค่าฟังก์ชันได้ แต่คุณไม่ควรถือว่าตัวแปรเหล่านี้เป็นวิธีที่ปลอดภัยในการจัดเก็บข้อมูลที่ละเอียดอ่อน เช่น ข้อมูลเข้าสู่ระบบฐานข้อมูลหรือคีย์ API ซึ่งสำคัญมากในกรณีที่คุณตรวจสอบไฟล์ .env ในการควบคุมแหล่งที่มา

Cloud Functions for Firebase ผสานรวมกับ Google Cloud Secret Manager เพื่อช่วยคุณจัดเก็บข้อมูลการกำหนดค่าที่ละเอียดอ่อน บริการที่เข้ารหัสนี้จะจัดเก็บค่าการกำหนดค่าอย่างปลอดภัย ขณะเดียวกันก็ยังช่วยให้เข้าถึงจากฟังก์ชันต่างๆ ได้อย่างง่ายดายเมื่อจำเป็น

สร้างและใช้ข้อมูลลับ

หากต้องการสร้างข้อมูลลับ ให้ใช้ Firebase CLI

วิธีสร้างและใช้ข้อมูลลับ

  1. เรียกใช้คำสั่งต่อไปนี้จากรูทของไดเรกทอรีโปรเจ็กต์ที่อยู่ในเครื่อง

    firebase functions:secrets:set SECRET_NAME

  2. ป้อนค่าสำหรับ SECRET_NAME

    CLI จะแสดงข้อความแจ้งว่าสำเร็จและเตือนว่าคุณต้องทำให้ฟังก์ชันใช้งานได้เพื่อให้การเปลี่ยนแปลงมีผล

  3. ก่อนทำให้ใช้งานได้ โปรดตรวจสอบว่าโค้ดฟังก์ชันอนุญาตให้ฟังก์ชันเข้าถึงข้อมูลลับโดยใช้ตัวเลือก secrets

    Node.js

    const { onRequest } = require('firebase-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

    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. ทำให้ Cloud Functions ใช้งานได้:

    firebase deploy --only functions

    ตอนนี้คุณจะเข้าถึงตัวแปรนี้ได้เหมือนกับตัวแปรสภาพแวดล้อมอื่นๆ ในทางกลับกัน หากฟังก์ชันอื่นที่ไม่ได้ระบุข้อมูลลับพยายามเข้าถึงข้อมูลลับ ฟังก์ชันดังกล่าวจะได้รับค่าที่ไม่ได้กำหนด ดังนี้

    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 `secrets` option is missing
    });
    

    Python

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

เมื่อติดตั้งใช้งานฟังก์ชันแล้ว ฟังก์ชันจะมีสิทธิ์เข้าถึงค่าข้อมูลลับ เฉพาะฟังก์ชันที่มีข้อมูลลับในตัวเลือก secrets เท่านั้นที่จะเข้าถึงข้อมูลลับนั้นเป็นตัวแปรสภาพแวดล้อมได้ ซึ่งจะช่วยให้มั่นใจได้ว่าค่าข้อมูลลับจะพร้อมใช้งานเฉพาะในกรณีที่จำเป็นเท่านั้น ซึ่งจะช่วยลดความเสี่ยงที่ข้อมูลลับจะรั่วไหลโดยไม่ตั้งใจ

การจัดการข้อมูลลับ

ใช้ Firebase CLI เพื่อจัดการข้อมูลลับ ขณะจัดการข้อมูลลับด้วยวิธีนี้ โปรดทราบว่าการเปลี่ยนแปลง CLI บางอย่างกำหนดให้คุณต้องแก้ไขและ/หรือทำให้ฟังก์ชันที่เกี่ยวข้องใช้งานได้อีกครั้ง กล่าวโดยละเอียดคือ

  • เมื่อใดก็ตามที่คุณตั้งค่าใหม่สำหรับข้อมูลลับ คุณต้องทำให้ฟังก์ชันทั้งหมดที่อ้างอิงข้อมูลลับนั้นใช้งานได้อีกครั้งเพื่อให้ฟังก์ชันเหล่านั้นรับค่าล่าสุด
  • หากคุณลบข้อมูลลับ โปรดตรวจสอบว่าไม่มีฟังก์ชันที่ติดตั้งใช้งานซึ่งอ้างอิงข้อมูลลับนั้น ฟังก์ชันที่ใช้ค่าลับที่ถูกลบไปแล้วจะดำเนินการไม่สำเร็จโดยไม่มีการแจ้งเตือน

สรุปคำสั่ง Firebase CLI สำหรับ Secret Manager มีดังนี้

# Change the value of an existing secret
firebase functions:secrets:set SECRET_NAME

# Set secret from file
firebase functions:secrets:set SECRET_NAME --data-file file.json

# Validate secret value as json
cat file.json | firebase functions:secrets:set SECRET_NAME --format=json

# Pipe from stdin and set secret
cat file.json | firebase functions:secrets:set SECRET_NAME --format=json

# 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

สำหรับคำสั่ง access และ destroy คุณสามารถระบุพารามิเตอร์เวอร์ชันที่ไม่บังคับเพื่อจัดการเวอร์ชันที่ต้องการได้ เช่น

functions:secrets:access SECRET_NAME[@VERSION]

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการดำเนินการเหล่านี้ ให้ส่ง -h พร้อมคำสั่งเพื่อดูความช่วยเหลือเกี่ยวกับ CLI

วิธีเรียกเก็บเงินสำหรับข้อมูลลับ

Secret Manager อนุญาตให้ใช้เวอร์ชันของข้อมูลลับที่ใช้งานอยู่ได้ 6 เวอร์ชันโดยไม่มีค่าใช้จ่าย ซึ่งหมายความว่าคุณสามารถมีข้อมูลลับได้ 6 รายการต่อเดือนในโปรเจ็กต์ Firebase โดยไม่มีค่าใช้จ่าย

โดยค่าเริ่มต้น CLI ของ Firebase จะพยายามทำลายเวอร์ชันของข้อมูลลับที่ไม่ได้ใช้โดยอัตโนมัติเมื่อเหมาะสม เช่น เมื่อคุณติดตั้งใช้งานฟังก์ชันที่มีข้อมูลลับเวอร์ชันใหม่ นอกจากนี้ คุณยังล้างข้อมูลลับที่ไม่ได้ใช้อย่างสม่ำเสมอได้โดยใช้ functions:secrets:destroy และ functions:secrets:prune

Secret Manager อนุญาตให้ดำเนินการเข้าถึงแบบไม่เรียกเก็บเงินรายเดือนได้ 10,000 ครั้งในข้อมูลลับ อินสแตนซ์ของฟังก์ชันจะอ่านเฉพาะข้อมูลลับที่ระบุไว้ในตัวเลือก secrets ทุกครั้งที่เริ่มทำงานแบบ Cold Start หากคุณมีอินสแตนซ์ฟังก์ชันจำนวนมากที่อ่านข้อมูลลับจำนวนมาก โปรเจ็กต์ของคุณอาจเกินค่าเผื่อนี้ ซึ่งจะมีการเรียกเก็บเงินจากคุณ $0.03 ต่อการดำเนินการเข้าถึง 10,000 ครั้ง

ดูข้อมูลเพิ่มเติมได้ที่ราคาของ Secret Manager

การรองรับโปรแกรมจำลอง

การกำหนดค่าสภาพแวดล้อมด้วย dotenv ออกแบบมาเพื่อทำงานร่วมกับโปรแกรมจำลอง Cloud Functions ในเครื่อง

เมื่อใช้โปรแกรมจำลอง Cloud Functions ในเครื่อง คุณสามารถลบล้างตัวแปรสภาพแวดล้อมสำหรับโปรเจ็กต์ได้โดยการตั้งค่าไฟล์ .env.local เนื้อหาของ .env.local มีความสำคัญเหนือกว่า .env และไฟล์ .env เฉพาะโปรเจ็กต์

ตัวอย่างเช่น โปรเจ็กต์อาจมีไฟล์ 3 ไฟล์ต่อไปนี้ซึ่งมีค่าแตกต่างกันเล็กน้อยสำหรับการพัฒนาและการทดสอบในเครื่อง

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

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Local Humans

เมื่อเริ่มต้นในบริบทเฉพาะรายการ โปรแกรมจำลองจะโหลดตัวแปรสภาพแวดล้อมดังที่แสดงต่อไปนี้

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

ข้อมูลลับและข้อมูลเข้าสู่ระบบในโปรแกรมจำลอง Cloud Functions

โปรแกรมจำลอง Cloud Functions รองรับการใช้ข้อมูลลับเพื่อจัดเก็บและเข้าถึงข้อมูลการกำหนดค่าที่ละเอียดอ่อน โดยค่าเริ่มต้น โปรแกรมจำลองจะพยายามเข้าถึงข้อมูลลับเวอร์ชันที่ใช้งานจริงโดยใช้ข้อมูลเข้าสู่ระบบเริ่มต้นของแอปพลิเคชัน ในบางสถานการณ์ เช่น สภาพแวดล้อม CI โปรแกรมจำลองอาจเข้าถึงค่าข้อมูลลับไม่ได้เนื่องจากข้อจำกัดด้านสิทธิ์

คุณสามารถลบล้างค่าข้อมูลลับได้โดยการตั้งค่าไฟล์ .secret.local ซึ่งคล้ายกับการรองรับตัวแปรสภาพแวดล้อมของโปรแกรมจำลอง Cloud Functions ซึ่งจะช่วยให้คุณทดสอบฟังก์ชันในเครื่องได้ง่าย โดยเฉพาะอย่างยิ่งหากคุณไม่มีสิทธิ์เข้าถึงค่าข้อมูลลับ

ย้ายข้อมูลจากการกำหนดค่ารันไทม์

functions.config API เลิกใช้งานแล้วและจะหยุดให้บริการในเดือนมีนาคม 2027 หลังจากวันที่ดังกล่าว การติดตั้งใช้งานด้วย functions.config จะไม่สำเร็จ

เพื่อป้องกันไม่ให้การติดตั้งใช้งานล้มเหลว ให้ย้ายข้อมูลการกำหนดค่าไปยัง Cloud Secret Manager โดยใช้ Firebase CLI เราขอแนะนำอย่างยิ่งให้ใช้วิธีนี้เนื่องจากเป็นวิธีที่มีประสิทธิภาพและปลอดภัยที่สุดในการย้ายข้อมูลการกำหนดค่า

  1. ส่งออกการกำหนดค่าด้วย Firebase CLI

    ใช้คำสั่ง config export เพื่อส่งออกการกำหนดค่าสภาพแวดล้อมที่มีอยู่ไปยังข้อมูลลับใหม่ใน 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. อัปเดตโค้ดฟังก์ชันเพื่อเชื่อมโยงข้อมูลลับ

    หากต้องการใช้การกำหนดค่าที่จัดเก็บไว้ในข้อมูลลับใหม่ใน Cloud Secret Manager ให้ใช้ API defineJsonSecret ในแหล่งที่มาของฟังก์ชัน นอกจากนี้ โปรดตรวจสอบว่าข้อมูลลับเชื่อมโยงกับฟังก์ชันทั้งหมดที่จำเป็นต้องใช้ข้อมูลลับ

    ก่อน

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

    หลัง

    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. ทำให้ฟังก์ชันใช้งานได้

    ทำให้ฟังก์ชันที่อัปเดตใช้งานได้เพื่อใช้การเปลี่ยนแปลงและเชื่อมโยงสิทธิ์ของข้อมูลลับ

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

ตัวแปรสภาพแวดล้อมที่สร้างขึ้นโดยอัตโนมัติ

มีตัวแปรสภาพแวดล้อมที่สร้างขึ้นโดยอัตโนมัติในรันไทม์ของฟังก์ชันและในฟังก์ชันที่จำลองในเครื่อง ซึ่งรวมถึงตัวแปรที่สร้างโดย Google Cloud และตัวแปรสภาพแวดล้อมเฉพาะของ Firebase ดังนี้

process.env.FIREBASE_CONFIG: ให้ข้อมูลการกำหนดค่าโปรเจ็กต์ Firebase ต่อไปนี้

{
  databaseURL: 'https://DATABASE_NAME.firebaseio.com',
  storageBucket: 'PROJECT_ID.firebasestorage.app',
  projectId: 'PROJECT_ID'
}

โปรดทราบว่าค่าในการกำหนดค่า Firebase จริงอาจแตกต่างกันไปตามทรัพยากรที่คุณจัดสรรไว้ในโปรเจ็กต์

ระบบจะใช้การกำหนดค่านี้โดยอัตโนมัติเมื่อคุณเริ่มต้น Firebase Admin SDK โดยไม่มีอาร์กิวเมนต์ หากคุณเขียนฟังก์ชันใน JavaScript ให้เริ่มต้นดังนี้

const admin = require('firebase-admin');
admin.initializeApp();

หากคุณเขียนฟังก์ชันใน TypeScript ให้เริ่มต้นดังนี้

import * as functions from 'firebase-functions/v1';
import * as admin from 'firebase-admin';
import 'firebase-functions/v1';
admin.initializeApp();

หากต้องการเริ่มต้น Admin SDK ด้วยการกำหนดค่าโปรเจ็กต์เริ่มต้นโดยใช้ข้อมูลเข้าสู่ระบบของบัญชีบริการ คุณสามารถโหลดข้อมูลเข้าสู่ระบบจากไฟล์และเพิ่มลงใน FIREBASE_CONFIG ได้ดังนี้

serviceAccount = require('./serviceAccount.json');

const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG);
adminConfig.credential = admin.credential.cert(serviceAccount);
admin.initializeApp(adminConfig);