ארגון מספר פונקציות


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

ארגון פונקציות בבסיסי קוד

אפשר להשתמש במאפיין codebase של אובייקט ההגדרה של הפונקציות ב-firebase.json כדי לנהל אוסף גדול של פונקציות בכמה מאגרים או בחבילות משנה במאגר יחיד של monorepo:

# firebase.json
"functions": {
  "codebase": "my-codebase"
  # NOTE: Codebase must be less than 63 characters and can contain only
  # lowercase letters, numeric characters, underscores, and dashes.
}

המאפיין codebase נתמך ב-Firebase CLI מגרסה 10.7.1 ואילך.

ניהול של כמה מאגרי קוד

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

$  tree .
├── repoA
│   ├── firebase.json
│   └── functions
│       ├── index.js
│       └── package.json
└── repoB
    ├── firebase.json
    └── functions
        ├── index.js
        └── package.json

בלי הערות בבסיס הקוד, Firebase CLI היה מציג בקשה למחיקת פונקציות שהוגדרו במאגר השני בזמן הפריסה:

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
  functions: functions folder uploaded successfully
The following functions are found in your project but do not exist in your local source code:
        fn1FromRepoB
        fn2FromRepoB
        ...
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N)

כדי להימנע מהבעיה הזו, צריך להוסיף הערה ייחודית לבסיס הקוד בקטע ההגדרות של הפונקציות ב-firebase.json בכל מאגר פרויקטים:

# repoA/firebase.json
"functions": {
  "codebase": "repo-a"
}

# repoB/firebase.json
"functions": {
  "codebase": "repo-b"
}

בעזרת הערות בבסיס הקוד, Firebase CLI לא יציג יותר בקשות למחיקת פונקציות שהוגדרו מחוץ למאגר המיידי:

$ (cd repoA && firebase deploy --only functions)
...
i  functions: preparing functions directory for uploading...
  functions: functions folder uploaded successfully
#  Gleefully ignores functions from repoB
i  functions: creating Node.js 16 function fnFromRepoA (us-central1)...
  Deploy Complete!

ניהול של כמה חבילות מקור (monorepo)

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

$  tree .
├── firebase.json
├── teamA
│   ├── index.js
│   └── package.json
└── teamB
    ├── index.js
    └── package.json

ההגדרה הזו מתאימה לתרחישי השימוש הבאים:

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

כדי לתמוך בהגדרת monorepo כמו זו, צריך להגדיר כמה הגדרות של פונקציות ב-firebase.json:

"functions": [
  {
    "source": "teamA",
    "codebase": "team-a"
  },
  {
    "source": "teamB",
    "codebase": "team-b"
  },
]

עם ההגדרה הזו, Firebase CLI פורס פונקציות מכל החבילות בפקודת פריסה אחת:

$ firebase deploy --only functions
i  deploying functions
i  functions: preparing codebase team-a for deployment
i  functions: preparing codebase team-b for deployment
i  functions: creating Node.js 16 function team-a:helloATeam(us-central1)...
i  functions: creating Node.js 16 function team-b:helloBTeam(us-central1)...
...

אפשר גם לפרוס בסיס קוד ספציפי:

$ firebase deploy --only functions:team-b
i  deploying functions
i  functions: preparing codebase team-b for deployment
i  functions: updating Node.js 16 function team-b:helloBTeam(us-central1)...
...

כתיבת פונקציות בכמה קבצים

כשמתחילים להשתמש ב-Cloud Functions, יכול להיות שתשימו את הפונקציות הראשונות שלכם בקובץ אחד:

index.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

main.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello bar!")

אם יש יותר מכמה פונקציות, קשה לנהל את זה. במקום זאת, אפשר להכניס את כל הלוגיקה של כל פונקציה לקובץ משלה ולהשתמש בקובץ המקור כרשימה של ייצואים:

Node.js

foo.js

const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});

bar.js

const functions = require('firebase-functions/v1');
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

index.js

const foo = require('./foo');
const bar = require('./bar');
exports.foo = foo.foo;
exports.bar = bar.bar;

Python

foo.py

from firebase_functions import https_fn

@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

bar.py

from firebase_functions import https_fn

@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
    return https_fn.Response("Hello foo!")

main.py

from fn_impl.foo import *
from fn_impl.bar import *

ההגדרה הזו מניחה מבנה של ספריית פרויקט כמו זה:

my-project
├── firebase.json
└── functions
    ├── fn_impl
    │   ├── __init__.py
    │   ├── foo.py
    │   └── bar.py
    ├── main.py
    └── requirements.txt

fn_impl: יכול להיות לו כל שם

__init__.py: חובה, אבל אפשר להשאיר ריק

פונקציות קבוצתיות

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

metrics.js


const functions = require('firebase-functions/v1');
exports.usageStats = functions.https.onRequest((request, response) => {
  // ...
});
exports.nightlyReport = functions.https.onRequest((request, response) => {
  // ...
});

אפשר להכניס את הפונקציות האלה לקבוצה כשמייצאים אותן בקובץ index.js:

index.js


// Export both functions from metrics.js in the "metrics" group:
//  - metrics-usageStats
//  - metrics-nightlyReport
exports.metrics = require('./metrics');

כשפורסים את הפונקציות, השם של הקבוצה יתווסף לתחילת השם של הפונקציה. לכן, בדוגמה הזו, השמות של הפונקציות יהיו metrics-usageStats ו-metrics-nightlyReport.

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


firebase deploy --only functions:metrics

השלבים הבאים

מידע נוסף על Cloud Functions זמין במאמרים הבאים: