שמירת נתונים

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

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

אבטחת נתונים

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

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