תחילת העבודה: כתיבה, בדיקה ופריסה של הפונקציות הראשונות


כדי להתחיל להשתמש ב-Cloud Functions, כדאי לנסות את המדריך הזה, שמתחיל במשימות ההגדרה הנדרשות וממשיך ביצירה, בבדיקות ובפריסה של שתי פונקציות קשורות:

  • פונקציית add message שחשפה כתובת URL שמקבלת ערך טקסט ומשמרת אותו ב-Cloud Firestore.
  • המרה מסוג 'אותיות רישיות' פונקציה שמופעלת על פעולת כתיבה של Cloud Firestore ומבצעת טרנספורמציה את הטקסט לאותיות רישיות.

זו הקוד לדוגמה המלא שמכיל את הפונקציות:

Node.js

// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const {logger} = require("firebase-functions");
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

// The Firebase Admin SDK to access Firestore.
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");

initializeApp();

// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
exports.addmessage = onRequest(async (req, res) => {
  // Grab the text parameter.
  const original = req.query.text;
  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await getFirestore()
      .collection("messages")
      .add({original: original});
  // Send back a message that we've successfully written the message
  res.json({result: `Message with ID: ${writeResult.id} added.`});
});

// Listens for new messages added to /messages/:documentId/original
// and saves an uppercased version of the message
// to /messages/:documentId/uppercase
exports.makeuppercase = onDocumentCreated("/messages/{documentId}", (event) => {
  // Grab the current value of what was written to Firestore.
  const original = event.data.data().original;

  // Access the parameter `{documentId}` with `event.params`
  logger.log("Uppercasing", event.params.documentId, original);

  const uppercase = original.toUpperCase();

  // You must return a Promise when performing
  // asynchronous tasks inside a function
  // such as writing to Firestore.
  // Setting an 'uppercase' field in Firestore document returns a Promise.
  return event.data.ref.set({uppercase}, {merge: true});
});

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import firestore_fn, https_fn

# The Firebase Admin SDK to access Cloud Firestore.
from firebase_admin import initialize_app, firestore
import google.cloud.firestore

app = initialize_app()


@https_fn.on_request()
def addmessage(req: https_fn.Request) -> https_fn.Response:
    """Take the text parameter passed to this HTTP endpoint and insert it into
    a new document in the messages collection."""
    # Grab the text parameter.
    original = req.args.get("text")
    if original is None:
        return https_fn.Response("No text parameter provided", status=400)

    firestore_client: google.cloud.firestore.Client = firestore.client()

    # Push the new message into Cloud Firestore using the Firebase Admin SDK.
    _, doc_ref = firestore_client.collection("messages").add({"original": original})

    # Send back a message that we've successfully written the message
    return https_fn.Response(f"Message with ID {doc_ref.id} added.")


@firestore_fn.on_document_created(document="messages/{pushId}")
def makeuppercase(event: firestore_fn.Event[firestore_fn.DocumentSnapshot | None]) -> None:
    """Listens for new documents to be added to /messages. If the document has
    an "original" field, creates an "uppercase" field containg the contents of
    "original" in upper case."""

    # Get the value of "original" if it exists.
    if event.data is None:
        return
    try:
        original = event.data.get("original")
    except KeyError:
        # No "original" field, so do nothing.
        return

    # Set the "uppercase" field.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    event.data.reference.update({"uppercase": upper})

מידע על המדריך הזה

בחרנו ב-Cloud Firestore ובפונקציות שמופעל בהן טריגר HTTP לדוגמה הזו, בין היתר כי אפשר לבדוק היטב את הטריגרים האלה ברקע באמצעות Firebase Local Emulator Suite. ערכת הכלים הזו תומכת גם בטריגרים של Realtime Database,‏ Cloud Storage,‏ PubSub,‏ Auth ו-HTTP callable. סוגים אחרים של טריגרים ברקע כמו Remote Config וטריגרים של TestLab יכולים להיות נבדקו באופן אינטראקטיבי באמצעות ערכות כלים שמתוארים בדף הזה.

בחלקים הבאים של המדריך מוסבר בפירוט איך יוצרים, בודקים ופורסים את הדוגמה.

יצירת פרויקט Firebase

  1. במסוף Firebase, לוחצים על Add project.

    • כדי להוסיף משאבים של Firebase לפרויקט Google Cloud קיים, מזינים את שם הפרויקט או בוחרים אותו בתפריט הנפתח.

    • כדי ליצור פרויקט חדש, מזינים את שם הפרויקט הרצוי. אפשר גם לבחור עורכים את מזהה הפרויקט שמוצג מתחת לשם הפרויקט.

  2. אם מופיעה בקשה, קוראים את התנאים של Firebase ומאשרים אותם.

  3. לוחצים על המשך.

  4. (אופציונלי) מגדירים את Google Analytics לפרויקט, וכך כדי ליהנות מחוויה אופטימלית בשימוש בכל אחד ממוצרי Firebase הבאים:

    בוחרים חשבון Google Analytics קיים או יוצרים חשבון חדש.

    אם יוצרים חשבון חדש, בוחרים את Analytics מיקום הדיווח ולאחר מכן מאשרים את ההגדרות של שיתוף הנתונים ואת התנאים של Google Analytics לפרויקט.

  5. לוחצים על יצירת פרויקט (או על הוספת Firebase, אם משתמשים פרויקט Google Cloud קיים).

מערכת Firebase מקצה משאבים באופן אוטומטי לפרויקט Firebase שלכם. בסיום התהליך, תועברו לדף הסקירה הכללית של הפרויקט ב-Firebase במסוף Firebase.

הגדרת הסביבה ו-Firebase CLI

Node.js

כדי לכתוב פונקציות צריך סביבת Node.js ויהיה צורך ב-CLI Firebase כדי לפרוס פונקציות את זמן הריצה Cloud Functions. להתקנת Node.js ו-npm, מנהל גרסאות הצומת מומלץ.

אחרי שמתקינים את Node.js ואת npm, מתקינים את ה-CLI של Firebase בשיטה המועדפת עליכם. כדי להתקין את ה-CLI באמצעות npm, צריך להשתמש ב:

npm install -g firebase-tools

הפקודה firebase זמינה בכל העולם. אם המיקום הפקודה תיכשל, אולי תצטרכו לשינוי ההרשאות של NPM. כדי לעדכן לגרסה האחרונה של firebase-tools, מריצים שוב את אותה הפקודה.

Python

נדרשת סביבת Python כדי לכתוב פונקציות, ויהיה צורך ב-CLI Firebase כדי לפרוס פונקציות את זמן הריצה Cloud Functions. מומלץ להשתמש ב-venv כדי לבודד יחסי תלות. הגרסאות 3.10 ו-3.11 של Python נתמך.

אחרי שמתקינים את Python, להתקין את ה-CLI של Firebase באמצעות השיטה המועדפת עליכם.

איך מפעילים את הפרויקט

כשמפעילים את Firebase SDK עבור Cloud Functions, נוצר פרויקט ריק שמכיל יחסי תלות וקוד לדוגמה מינימלי. אם אתם משתמשים ב-Node.js, תוכלו לבחור ב-TypeScript או ב-JavaScript כדי ליצור פונקציות. לצורך המדריך הזה, תצטרכו גם לאתחל את Cloud Firestore.

כדי לאתחל את הפרויקט:

  1. מריצים את firebase login כדי להתחבר דרך הדפדפן ולאמת את Firebase CLI.
  2. נכנסים לספריית הפרויקט ב-Firebase.
  3. מריצים את firebase init firestore. במדריך הזה, אפשר לאשר את ערכי ברירת המחדל כשמתבקשים להזין כללים לקובצי אינדקס ב-Firestore. אם עדיין לא השתמשתם ב-Cloud Firestore בפרויקט הזה, תצטרכו גם לבחור מצב התחלה ומיקום ל-Firestore, כפי שמתואר במאמר תחילת העבודה עם Cloud Firestore.
  4. מריצים את firebase init functions. ב-CLI תתבקשו לבחור בסיס קוד קיים או לאתחל בסיס קוד חדש ולתת לו שם. כשרק מתחילים, מספיק בסיס קוד אחד במיקום ברירת המחדל. בהמשך, ככל שההטמעה תתרחב, כדאי לארגן את הפונקציות בבסיס קוד.
  5. ב-CLI יש את האפשרויות הבאות לקבלת תמיכה בשפה:

    • JavaScript
    • TypeScript
    • Python

    למדריך הזה, בוחרים באפשרות JavaScript או Python. עבור יצירה ב- TypeScript, ראו כתיבת פונקציות באמצעות TypeScript.

  6. ב-CLI אפשר להתקין יחסי תלות. אפשר לדחות את הבקשה הזו אם רוצים לנהל את יחסי התלות בדרך אחרת.

אחרי שהפקודות האלה יושלמו, מבנה הפרויקט ייראה כך:

Node.js

myproject
+- .firebaserc    # Hidden file that helps you quickly switch between
|                 # projects with `firebase use`
|
+- firebase.json  # Describes properties for your project
|
+- functions/     # Directory containing all your functions code
      |
      +- .eslintrc.json  # Optional file containing rules for JavaScript linting.
      |
      +- package.json  # npm package file describing your Cloud Functions code
      |
      +- index.js      # Main source file for your Cloud Functions code
      |
      +- node_modules/ # Directory where your dependencies (declared in
                        # package.json) are installed

ב-Node.js, הקובץ package.json שנוצר במהלך האתחול מכיל מפתח חשוב: "engines": {"node": "18"}. כאן מציינים את גרסת Node.js לכתיבה ולפריסה של פונקציות. אפשר בוחרים גרסאות נתמכות אחרות.

Python

myproject
+- .firebaserc    # Hidden file that helps you quickly switch between
|                 # projects with `firebase use`
|
+- firebase.json  # Describes properties for your project
|
+- functions/     # Directory containing all your functions code
      |
      +- main.py      # Main source file for your Cloud Functions code
      |
      +- requirements.txt  #  List of the project's modules and packages 
      |
      +- venv/ # Directory where your dependencies are installed

ייבוא המודולים הנדרשים והפעלת אפליקציה

אחרי שתסיימו את משימות ההגדרה, תוכלו לפתוח את ספריית המקור ולהתחיל להוסיף קוד, כפי שמתואר בקטעים הבאים. בדוגמה הזו, צריך לייבא לפרויקט את המודולים Cloud Functions ו-Admin SDK. הוספת שורות כמו בקובץ המקור:

Node.js

// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const {logger} = require("firebase-functions");
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

// The Firebase Admin SDK to access Firestore.
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");

initializeApp();

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import firestore_fn, https_fn

# The Firebase Admin SDK to access Cloud Firestore.
from firebase_admin import initialize_app, firestore
import google.cloud.firestore

app = initialize_app()

השורות האלה טוענות את המודולים הנדרשים ומפעילות מכונה של אפליקציית admin שממנה אפשר לבצע שינויים ב-Cloud Firestore. בכל מקום שבו יש תמיכה ב-Admin SDK, כמו שהיא עבור FCM, Authentication ו-Firebase Realtime Database, הוא מספק מאוד לשלב את Firebase באמצעות Cloud Functions.

Firebase CLI באופן אוטומטי מתקין את ה-SDK של Firebase Admin ואת ה-SDK של Firebase עבור המודולים של Cloud Functions בזמן האתחול בפרויקט שלכם. למידע נוסף על הוספת ספריות של צד שלישי לפרויקט שלכם: יחסי תלות של הכינוי.

מוסיפים את הפונקציה add message

בשביל 'Add message' מוסיפים את השורות הבאות לקובץ המקור:

Node.js

// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
exports.addmessage = onRequest(async (req, res) => {
  // Grab the text parameter.
  const original = req.query.text;
  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await getFirestore()
      .collection("messages")
      .add({original: original});
  // Send back a message that we've successfully written the message
  res.json({result: `Message with ID: ${writeResult.id} added.`});
});

Python

@https_fn.on_request()
def addmessage(req: https_fn.Request) -> https_fn.Response:
    """Take the text parameter passed to this HTTP endpoint and insert it into
    a new document in the messages collection."""
    # Grab the text parameter.
    original = req.args.get("text")
    if original is None:
        return https_fn.Response("No text parameter provided", status=400)

    firestore_client: google.cloud.firestore.Client = firestore.client()

    # Push the new message into Cloud Firestore using the Firebase Admin SDK.
    _, doc_ref = firestore_client.collection("messages").add({"original": original})

    # Send back a message that we've successfully written the message
    return https_fn.Response(f"Message with ID {doc_ref.id} added.")

הפונקציה add message היא נקודת קצה (endpoint) של HTTP. כל בקשה לנקודת הקצה יובילו לאובייקטים של בקשה ותגובה שמועברים הגורם המטפל בבקשות לפלטפורמה שלכם (onRequest() או on_request).

פונקציות HTTP הן סינכרוניות (דומה ל- פונקציות קריאה), לכן עליכם לשלוח תשובה במהירות האפשרית ודחייה של העבודה באמצעות Cloud Firestore. פונקציית ה-HTTP 'add message' מעבירה ערך טקסט לנקודת הקצה של ה-HTTP ומוסיפה אותו למסד הנתונים בנתיב /messages/:documentId/original.

הוספת ההגדרה 'אותיות רישיות' פונקציה

לאותיות רישיות, מוסיפים את השורות הבאות לקובץ המקור:

Node.js

// Listens for new messages added to /messages/:documentId/original
// and saves an uppercased version of the message
// to /messages/:documentId/uppercase
exports.makeuppercase = onDocumentCreated("/messages/{documentId}", (event) => {
  // Grab the current value of what was written to Firestore.
  const original = event.data.data().original;

  // Access the parameter `{documentId}` with `event.params`
  logger.log("Uppercasing", event.params.documentId, original);

  const uppercase = original.toUpperCase();

  // You must return a Promise when performing
  // asynchronous tasks inside a function
  // such as writing to Firestore.
  // Setting an 'uppercase' field in Firestore document returns a Promise.
  return event.data.ref.set({uppercase}, {merge: true});
});

Python

@firestore_fn.on_document_created(document="messages/{pushId}")
def makeuppercase(event: firestore_fn.Event[firestore_fn.DocumentSnapshot | None]) -> None:
    """Listens for new documents to be added to /messages. If the document has
    an "original" field, creates an "uppercase" field containg the contents of
    "original" in upper case."""

    # Get the value of "original" if it exists.
    if event.data is None:
        return
    try:
        original = event.data.get("original")
    except KeyError:
        # No "original" field, so do nothing.
        return

    # Set the "uppercase" field.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    event.data.reference.update({"uppercase": upper})

הפונקציה make uppercase פועלת כשכותבים ל-Cloud Firestore, ומגדירה את המסמך שעליו מתבצע האזנה. מטעמי ביצועים, כדאי להיות ספציפיים ככל האפשר.

סוגריים מסולסלים – לדוגמה, {documentId} – מקיפים 'פרמטרים', תווים כלליים שמציגים את הנתונים התואמים שלהם בקריאה החוזרת. Cloud Firestore מפעיל את פונקציית ה-callback בכל פעם שמתווספות הודעות חדשות.

ב-Node.js, פונקציות מבוססות-אירועים כמו אירועי Cloud Firestore הן אסינכרוניות. פונקציית ההתקשרות החוזרת צריכה להחזיר null, אובייקט או Promise. אם לא מחזירים דבר, הזמן הקצוב לתפוגה של הפונקציה הוא 'שגיאה', יבוצע ניסיון חוזר. Sync,‏ Async ו-Promises

הדמיה של הפעלת הפונקציות

Firebase Local Emulator Suite מאפשר לפתח ולבדוק אפליקציות במחשב המקומי במקום לפרוס יש לכם פרויקט Firebase. מומלץ מאוד לבצע בדיקה מקומית במהלך הפיתוח, בין היתר כי היא מפחיתה את הסיכון לשגיאות תכנות שעלולות לגרום לעלויות בסביבת הייצור (לדוגמה, לולאה אינסופית).

כדי ליצור אמולציה של הפונקציות:

  1. מריצים את firebase emulators:start ובודקים את הפלט כדי למצוא את כתובת ה-URL של ה-Emulator Suite UI. ברירת המחדל היא localhost:4000, אבל יכול להיות שהיא תתארח ביציאה אחרת במכונה. מזינים את כתובת ה-URL הזו בדפדפן כדי לפתוח את Emulator Suite UI.

  2. בדיקת הפלט של firebase emulators:start הפקודה לגבי כתובת ה-URL של פונקציית HTTP. הכתובת תיראה דומה ל- http://localhost:5001/MY_PROJECT/us-central1/addMessage, למעט במקרים הבאים:

    1. המערכת תחליף את MY_PROJECT במזהה הפרויקט.
    2. יכול להיות שהיציאה תהיה שונה במחשב המקומי.
  3. מוסיפים את מחרוזת השאילתה ?text=uppercaseme לסוף כתובת ה-URL של הפונקציה. הוא אמור להיראות כך: http://localhost:5001/MY_PROJECT/us-central1/addMessage?text=uppercaseme. אפשר גם לשנות את ההודעה "uppercaseme" להתאמה אישית הודעה.

  4. כדי ליצור הודעה חדשה, פותחים את כתובת ה-URL בכרטיסייה חדשה בדפדפן.

  5. אפשר לראות את ההשפעות של הפונקציות ב-Emulator Suite UI:

    1. בכרטיסייה Logs אמורים להופיע יומנים חדשים שמציינים שפונקציות ה-HTTP פועלות בהצלחה:

      i functions: Beginning execution of "addMessage"

      i functions: Beginning execution of "makeUppercase"

    2. בכרטיסייה Firestore אמור להופיע מסמך שמכיל את ההודעה המקורית וגם את הגרסה של ההודעה עם אותיות רישיות (אם ההודעה המקורית הייתה 'uppercaseme', תוצג 'UPPERCASEME').

פריסת פונקציות בסביבת הייצור

לאחר שהפונקציות פועלות באופן הרצוי באמולטור, אפשר להמשיך אל בפריסה, בבדיקה ובהרצה בסביבת הייצור. חשוב לזכור לפרוס בסביבת הייצור, את הפרויקט חייבים להיות בתוכנית התמחור והתשלומים של Bllaze. צפייה תמחור של Cloud Functions.

כדי להשלים את המדריך, תצטרכו לפרוס את הפונקציות ולאחר מכן להריץ אותן.

  1. מריצים את הפקודה הבאה כדי לפרוס את הפונקציות:

     firebase deploy --only functions
     

    אחרי הרצת הפקודה הזו, ה-CLI של Firebase יפיק את כתובת ה-URL של כל נקודות הקצה של פונקציות ה-HTTP. במסוף אמורה להופיע שורה כזו:

    Function URL (addMessage): https://us-central1-MY_PROJECT.cloudfunctions.net/addMessage
    

    כתובת ה-URL מכילה את מזהה הפרויקט ואת האזור של פונקציית ה-HTTP. אין צורך לדאוג לזה כרגע, אבל בחלק מהפונקציות של HTTP בסביבת הייצור צריך לציין מיקום כדי למזער את זמן האחזור ברשת.

    אם נתקלתם בשגיאות גישה כמו 'לא ניתן לאשר גישה לפרויקט', נסו לבדוק את הכינוי של הפרויקט.

  2. באמצעות הפלט של כתובת ה-URL שה-CLI יצר, מוסיפים פרמטר של שאילתת טקסט, ופותחים אותה בדפדפן:

    https://us-central1-MY_PROJECT.cloudfunctions.net/addMessage?text=uppercasemetoo
    

    הפונקציה עורכת ומפנה את הדפדפן אל המסוף Firebase במיקום של מסד הנתונים שבו מאוחסנת מחרוזת הטקסט. אירוע הכתיבה הזה מפעיל את הפונקציה make uppercase, שמכתיבה גרסה של המחרוזת באותיות רישיות.

אחרי פריסה והפעלה של פונקציות, אפשר לראות יומנים במסוף Google Cloud. אם צריך למחוק פונקציות בפיתוח או בייצור, צריך להשתמש ב-CLI Firebase.

בסביבת הייצור, כדאי להגדיר מספר מינימלי ומקסימלי של מכונות להפעלה כדי לבצע אופטימיזציה של ביצועי הפונקציה ולשלוט בעלויות. צפייה שליטה בהתנהגות ההתאמה לעומס (scaling) לקבלת מידע נוסף על אפשרויות זמן הריצה האלה.

השלבים הבאים

במסמך הזה מוסבר איך לנהל פונקציות ב-Cloud Functions, ואיך לטפל בכל סוגי האירועים שנתמכים ב-Cloud Functions.

כדי לקבל מידע נוסף על Cloud Functions, יכול לבצע גם את הפעולות הבאות: