שמירת נתונים

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

שים כתיבת או החלפת נתונים בנתיב מוגדר, כמו 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 אם הבקשה תתבצע בהצלחה. הבקשות GET,‏ PUT,‏ POST ו-PATCH תומכות ב-print=silent.

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

אפשר לכתוב ערכים של שרתים במיקום באמצעות ערך 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 Service Unavailable מסד הנתונים בזמן אמת ב-Firebase שצוין לא זמין באופן זמני. המשמעות היא שלא נעשה ניסיון לשלוח את הבקשה.

אבטחת נתונים

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

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