שמירת נתונים

דרכים לחיסכון בנתונים

שים כתיבה או החלפה של נתונים בנתיב מוגדר, כמו fireblog/users/user1/<data>
תיקון עדכון של חלק מהמפתחות לנתיב מוגדר בלי להחליף את כל הנתונים.
פרסם להוסיף לרשימת נתונים במסד הנתונים של Firebase. בכל פעם ששולחים בקשה מסוג POST, לקוח Firebase יוצר מפתח ייחודי, כמו fireblog/users/<unique-id>/<data>
מחיקה הסרת נתונים מהפנייה שצוינה למסד הנתונים של Firebase.

כתיבת נתונים באמצעות PUT

פעולת הכתיבה הבסיסית דרך ה-API ל-REST היא PUT. כדי להמחיש את שמירת הנתונים, נבנה אפליקציית ניהול בלוגים עם פוסטים ומשתמשים. כל הנתונים של האפליקציה שלנו יישמרו בנתיב fireblog, בכתובת ה-URL של מסד הנתונים של Firebase: https://docs-examples.firebaseio.com/fireblog.

נתחיל בשמירת נתוני משתמשים מסוימים במסד הנתונים של Firebase. נאחסן כל משתמש לפי שם משתמש ייחודי, וגם נשמור את השם המלא ותאריך הלידה שלו. מאחר שלכל משתמש יהיה שם משתמש ייחודי, מומלץ להשתמש כאן ב-PUT במקום ב-POST, כי המפתח כבר קיים ואין צורך ליצור מפתח חדש.

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

curl -X PUT -d '{
  "alanisawesome": {
    "name": "Alan Turing",
    "birthday": "June 23, 1912"
  }
}' 'https://docs-examples.firebaseio.com/fireblog/users.json'

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

curl -X PUT -d '"Alan Turing"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/name.json'
curl -X PUT -d '"June 23, 1912"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/birthday.json'

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

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    }
  }
}

בקשה שמבוצעת בהצלחה יצוין באמצעות קוד מצב HTTP 200 OK, והתשובה תכיל את הנתונים שכתבתם למסד הנתונים. הדוגמה הראשונה תפעיל רק אירוע אחד אצל לקוחות שצופים בנתונים, ואילו הדוגמה השנייה תפעיל שניים. חשוב לציין שאם כבר קיימים נתונים בנתיב המשתמשים, הגישה הראשונה תחליף אותם, אבל השיטה השנייה תשנה רק את הערך של כל צומת צאצא נפרד, ותשאיר את הצאצאים האחרים ללא שינוי. הפונקציה PUT שוות ערך לפונקציה set() ב-JavaScript SDK שלנו.

עדכון נתונים באמצעות PATCH

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

curl -X PATCH -d '{
  "nickname": "Alan The Machine"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

הבקשה שלמעלה תכתוב את nickname באובייקט alanisawesome שלנו, בלי למחוק את הצאצאים name או birthday. לתשומת ליבך, אם היינו שולחים כאן בקשת PUT במקום זאת, name ו-birthday היו נמחקים כי הם לא נכללו בבקשה. הנתונים במסד הנתונים של Firebase נראים עכשיו כך:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    }
  }
}

בקשה שהצליחה תסומן בקוד סטטוס HTTP‏ 200 OK, והתגובה תכיל את הנתונים המעודכנים שנכתבו במסד הנתונים.

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

curl -X PATCH -d '{
  "alanisawesome/nickname": "Alan The Machine",
  "gracehopper/nickname": "Amazing Grace"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

אחרי העדכון הזה, גם לאלון וגם לגרציה נוספו הכינוי שלהם:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

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

curl -X PATCH -d '{
  "alanisawesome": {"nickname": "Alan The Machine"},
  "gracehopper": {"nickname": "Amazing Grace"}
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

כתוצאה מכך, מתקבלת התנהגות שונה, כלומר כתיבה מחדש של כל צומת /fireblog/users:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

עדכון נתונים באמצעות בקשות מותנות

אפשר להשתמש בבקשות מותנות, המקבילה לטרנזקציות ב-REST, כדי לעדכן את הנתונים בהתאם למצב הקיים שלהם. לדוגמה, אם רוצים להגדיל את מספר הלייקים ורוצים לוודא שהספירה משקפת באופן מדויק יותר מכמה לייקים בו-זמנית, אפשר להשתמש בבקשה מותנית כדי לכתוב את הערך החדש למונה. במקום שתי פעולות כתיבה שמחליפות את המונה לאותו מספר, אחת מבקשות הכתיבה נכשלת ואז אפשר לנסות שוב את הבקשה עם הערך החדש.
  1. כדי לבצע בקשה מותנית במיקום מסוים, צריך לקבל את המזהה הייחודי של הנתונים הנוכחיים במיקום הזה, או את ה-ETag. אם הנתונים משתנים במיקום הזה, גם ה-ETag משתנה. אפשר לבקש ETag בכל שיטה חוץ מ-PATCH. בדוגמה הבאה נעשה שימוש בבקשה מסוג GET.
    curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
    קריאה ספציפית ל-ETag בכותרת מחזירה את ה-ETag של המיקום שצוין בתגובה ל-HTTP.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    10 // Current value of the data at the specified location
  2. כדי לעדכן נתונים שתואמים באופן ספציפי לערך ה-ETag הזה, צריך לכלול את ה-ETag שהוחזר בבקשה הבאה של PUT או DELETE. בהמשך לדוגמה שלנו, כדי לעדכן את המונה ל-11, או לערך גבוה ב-1 מהערך הראשוני שאוחזר (10), ולהכשיל את הבקשה אם הערך כבר לא תואם, צריך להשתמש בקוד הבא:
    curl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
    אם הערך של הנתונים במיקום שצוין עדיין הוא 10, ה-ETag בבקשה PUT תואם והבקשה תצליח, והערך 11 יירשם במסד הנתונים.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    Cache-Control: no-cache
    
    11 // New value of the data at the specified location, written by the conditional request
    אם המיקום כבר לא תואם ל-ETag, מצב שעלול לקרות אם משתמש אחר כתב ערך חדש במסד הנתונים, הבקשה תיכשל בלי לכתוב למיקום. התשובה שתקבלו תכלול את הערך החדש ואת ה-ETag.
    HTTP/1.1 412 Precondition Failed
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    12 // New value of the data at the specified location
  3. אם תחליטו לנסות שוב את הבקשה, תוכלו להשתמש במידע החדש. Realtime Database לא ינסה מחדש באופן אוטומטי בקשות מותנות שנכשלו. עם זאת, אפשר להשתמש בערך החדש וב-ETag כדי ליצור בקשה מותנית חדשה עם המידע שהוחזר בתגובה לכישלון.

בקשות מותנות מבוססות-REST מיישמות את התקן if-match של HTTP. עם זאת, הם שונים מהסטנדרט בדרכים הבאות:

  • אפשר לציין רק ערך ETag אחד לכל בקשה של if-match, ולא כמה בקשות.
  • הסטנדרט מציע להחזיר את ה-ETags עם כל הבקשות, אבל ב-Realtime Database ה-ETags מוחזרים רק עם בקשות שכוללות את הכותרת X-Firebase-ETag. כך אפשר לצמצם את עלויות החיוב על בקשות רגילות.

יכול להיות גם שזמן האחזור של בקשות מותנות יהיה ארוך יותר מזה של בקשות REST רגילות.

שמירת רשימות של נתונים

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

curl -X POST -d '{
  "author": "alanisawesome",
  "title": "The Turing Machine"
}' 'https://docs-examples.firebaseio.com/fireblog/posts.json'

הנתיב posts מכיל עכשיו את הנתונים הבאים:

{
  "posts": {
    "-JSOpn9ZC54A4P4RoqVa": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

שימו לב שהמפתח -JSOpn9ZC54A4P4RoqVa נוצר עבורנו באופן אוטומטי כי השתמשנו בבקשה מסוג POST. בקשה שהצליחה תסומן בקוד סטטוס HTTP‏ 200 OK, והתגובה תכלול את המפתח של הנתונים החדשים שנוספו:

{"name":"-JSOpn9ZC54A4P4RoqVa"}

הסרת נתונים

כדי להסיר נתונים ממסד הנתונים, אפשר לשלוח בקשה מסוג DELETE עם כתובת ה-URL של הנתיב שממנו רוצים למחוק את הנתונים. הפקודה הבאה תמחק את Alan מהנתיב users:

curl -X DELETE \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

בקשת DELETE שהצליחה תסומן בקוד סטטוס HTTP‏ 200 OK עם תגובה שמכילה JSON‏ null.

פרמטרים של URI

ה-API ל-REST מקבל את פרמטרי ה-URI הבאים כשכותבים נתונים למסד הנתונים:

auth

פרמטר הבקשה auth מאפשר גישה לנתונים שמוגנים על ידי Firebase Realtime Database Security Rules, והוא נתמך בכל סוגי הבקשות. הארגומנט יכול להיות הסוד של האפליקציה ב-Firebase או אסימון אימות, שבו נעסוק בקטע הרשאת משתמש. בדוגמה הבאה שולחים בקשה מסוג POST עם הפרמטר auth, כאשר CREDENTIAL הוא הסוד של האפליקציה ב-Firebase או אסימון אימות:

curl -X POST -d '{"Authenticated POST request"}' \
  'https://docs-examples.firebaseio.com/auth-example.json?auth=CREDENTIAL'

הדפס

הפרמטר print מאפשר לנו לציין את הפורמט של התשובה שלנו ממסד הנתונים. הוספת print=pretty לבקשה שלנו תחזיר את הנתונים בפורמט קריא לבני אדם. print=pretty נתמך על ידי הבקשות GET,‏ PUT,‏ POST,‏ PATCH ו-DELETE.

כדי להשבית את הפלט מהשרת בזמן כתיבת נתונים, אפשר להוסיף את print=silent לבקשה שלנו. אם הבקשה תאושר, התגובה שתתקבל תהיה ריקה ותכלול את קוד הסטטוס 204 No Content של HTTP. השדה print=silent נתמך על ידי הבקשות GET, PUT, POST ו-PATCH.

כתיבת ערכים בשרת

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

curl -X PUT -d '{".sv": "timestamp"}' \
  'https://docs-examples.firebaseio.com/alanisawesome/createdAt.json'

"timestamp" הוא הערך היחיד של השרת שנתמך, והוא מייצג את הזמן מאז תחילת זמן יוניקס (UNIX epoch) במיליוניות השנייה.

שיפור ביצועי כתיבה

אם אנחנו כותבים כמויות גדולות של נתונים למסד הנתונים, אפשר להשתמש בפרמטר print=silent כדי לשפר את ביצועי הכתיבה ולהפחית את השימוש ברוחב הפס. בהתנהגות הכתיבה הרגילה, השרת משיב עם נתוני ה-JSON שנכתבו. כשמציינים את הערך print=silent, השרת סוגר את החיבור באופן מיידי אחרי קבלת הנתונים, וכך מפחית את השימוש ברוחב הפס.

במקרים שבהם אנחנו שולחים בקשות רבות למסד הנתונים, אפשר להשתמש שוב בחיבור HTTPS על ידי שליחת בקשת Keep-Alive בכותרת ה-HTTP.

תנאי שגיאה

ה-API ל-REST יחזיר קודי שגיאה בנסיבות הבאות:

קודי מצב HTTP
400 בקשה לא תקינה

אחד מתנאי השגיאה הבאים:

  • לא ניתן לנתח נתונים של PUT או POST.
  • חסרים נתונים של PUT או POST.
  • הבקשה מנסה PUT או POST נתונים גדולים מדי.
  • הקריאה ל-API ל-REST מכילה שמות צאצא לא חוקיים כחלק מהנתיב.
  • נתיב הקריאה ל-API ל-REST ארוך מדי.
  • הבקשה מכילה ערך שרת לא מזוהה.
  • האינדקס של השאילתה לא מוגדר ב-Firebase Realtime Database Security Rules.
  • הבקשה לא תומכת באחד מהפרמטרים של השאילתה שצוינו.
  • הבקשה משלבת פרמטרים של שאילתה עם בקשת GET רדודה.
401 Unauthorized

אחד מתנאי השגיאה הבאים:

  • פג התוקף של אסימון האימות.
  • טוקן האימות שנעשה בו שימוש בבקשה לא תקין.
  • האימות באמצעות access_token נכשל.
  • הבקשה מפירה את Firebase Realtime Database Security Rules שלך.
404 לא נמצא מסד הנתונים של Firebase שצוין לא נמצא.
500 שגיאת שרת פנימית השרת החזיר שגיאה. פרטים נוספים מופיעים בהודעת השגיאה.
503 שירות לא זמין מסד הנתונים בזמן אמת ב-Firebase שצוין לא זמין באופן זמני, כלומר לא בוצעה ניסיון שליחת בקשה.

אבטחת נתונים

ב-Firebase יש שפת אבטחה שמאפשרת לנו להגדיר לאילו משתמשים יש גישת קריאה וכתיבה בצמתים שונים של הנתונים שלנו. מידע נוסף זמין במאמר Realtime Database Security Rules.

אחרי שסיפרנו איך שומרים נתונים, נלמד בקטע הבא איך לאחזר את הנתונים שלנו ממסד הנתונים של Firebase דרך ה-API ל-REST.