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


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

ארגון פונקציות ב-codebases

אפשר להשתמש במאפיין 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

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

$ (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, וצוותים שונים מנהלים את הגדרות הפונקציות שלהם בחבילה מבודדת.
  • יש לכם פונקציה עם תלות חיצונית כבדה והפעלה ראשונית ממושכת, ואתם רוצים לבודד את הפונקציה הזו מפונקציות אחרות שרגישות לזמן אחזור.

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

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

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

$ 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 זמין במאמרים הבאים: