वेब पर डेटा पढ़ें और लिखें

(ज़रूरी नहीं) Firebase Local Emulator Suite की मदद से प्रोटोटाइप बनाना और जांच करना

आपका ऐप्लिकेशन Realtime Database से कैसे पढ़ता है और उसमें कैसे लिखता है, इस बारे में बात करने से पहले, Realtime Database की सुविधाओं के प्रोटोटाइप बनाने और उनकी जांच करने के लिए इस्तेमाल किए जा सकने वाले टूल के बारे में बताते हैं: Firebase Local Emulator Suite. अगर आपको अलग-अलग डेटा मॉडल आज़माने हैं, सुरक्षा नियमों को ऑप्टिमाइज़ करना है या बैक-एंड के साथ इंटरैक्ट करने का सबसे किफ़ायती तरीका ढूंढना है, तो लाइव सेवाओं को डिप्लॉय किए बिना स्थानीय तौर पर काम करना एक अच्छा आइडिया हो सकता है.

Realtime Database एमुलेटर, Local Emulator Suite का हिस्सा होता है. इससे आपके ऐप्लिकेशन को, एमुलेट किए गए डेटाबेस के कॉन्टेंट और कॉन्फ़िगरेशन के साथ-साथ, एमुलेट किए गए प्रोजेक्ट के रिसॉर्स (फ़ंक्शन, अन्य डेटाबेस, और सुरक्षा नियम) के साथ इंटरैक्ट करने में मदद मिलती है.

Realtime Database एमुलेटर का इस्तेमाल करने के लिए, ये चरण पूरे करें:

  1. एम्युलेटर से कनेक्ट करने के लिए, अपने ऐप्लिकेशन के टेस्ट कॉन्फ़िगरेशन में कोड की एक लाइन जोड़ना.
  2. अपनी लोकल प्रोजेक्ट डायरेक्ट्री के रूट से, firebase emulators:start चलाएं.
  3. अपने ऐप्लिकेशन के प्रोटोटाइप कोड से कॉल करना. इसके लिए, Realtime Database प्लैटफ़ॉर्म SDK का इस्तेमाल हमेशा की तरह करें या Realtime Database REST API का इस्तेमाल करें.

Realtime Database और Cloud Functions के बारे में ज़्यादा जानकारी वाला वॉकथ्रू उपलब्ध है. आपको Local Emulator Suite के बारे में जानकारी भी देखनी चाहिए.

डेटाबेस का रेफ़रंस पाना

डेटाबेस से डेटा पढ़ने या उसमें डेटा लिखने के लिए, आपके पास firebase.database.Reference का एक इंस्टेंस होना चाहिए:

WebWeb
import { getDatabase } from "firebase/database";

const database = getDatabase();
var database = firebase.database();

डेटा सेव करना

इस दस्तावेज़ में, डेटा को वापस पाने के बुनियादी तरीके के साथ-साथ, Firebase डेटा को क्रम से लगाने और फ़िल्टर करने का तरीका बताया गया है.

firebase.database.Reference पर असाइनोसाइनस लिसनर अटैच करके, Firebase डेटा को वापस लाया जाता है. डेटा की शुरुआती स्थिति के लिए, Listener को एक बार ट्रिगर किया जाता है. इसके बाद, डेटा में बदलाव होने पर भी उसे ट्रिगर किया जाता है.

डेटा लिखने से जुड़ी बुनियादी कार्रवाइयां

डेटा को किसी तय रेफ़रंस में सेव करने के लिए, set() का इस्तेमाल किया जा सकता है. इससे उस पाथ में मौजूद किसी भी मौजूदा डेटा को बदल दिया जाता है. उदाहरण के लिए, कोई सोशल ब्लॉगिंग ऐप्लिकेशन, set() के साथ उपयोगकर्ता को इस तरह जोड़ सकता है:

WebWeb
import { getDatabase, ref, set } from "firebase/database";

function writeUserData(userId, name, email, imageUrl) {
  const db = getDatabase();
  set(ref(db, 'users/' + userId), {
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}
function writeUserData(userId, name, email, imageUrl) {
  firebase.database().ref('users/' + userId).set({
    username: name,
    email: email,
    profile_picture : imageUrl
  });
}

set() का इस्तेमाल करने पर, तय की गई जगह पर मौजूद डेटा को बदल दिया जाता है. इसमें चाइल्ड नोड भी शामिल हैं.

डेटा पढ़ना

वैल्यू इवेंट को सुनना

किसी पाथ पर डेटा पढ़ने और बदलावों को सुनने के लिए, इवेंट को देखने के लिए onValue() का इस्तेमाल करें. इस इवेंट का इस्तेमाल करके, किसी दिए गए पाथ पर मौजूद कॉन्टेंट के स्टैटिक स्नैपशॉट को पढ़ा जा सकता है. ये स्नैपशॉट, इवेंट के समय मौजूद होते हैं. यह तरीका, ऑडियंस को अटैच करने पर एक बार ट्रिगर होता है. इसके बाद, बच्चों के डेटा में हर बार बदलाव होने पर भी यह ट्रिगर होता है. इवेंट कॉलबैक को एक स्नैपशॉट भेजा जाता है. इसमें उस जगह का सारा डेटा होता है. इसमें चाइल्ड डेटा भी शामिल होता है. अगर कोई डेटा नहीं है, तो स्नैपशॉट पर exists() कॉल करने पर false और val() कॉल करने पर null दिखेगा.

यहां दिए गए उदाहरण में, डेटाबेस से किसी पोस्ट पर मिले स्टार की संख्या को वापस लाने वाले सोशल ब्लॉगिंग ऐप्लिकेशन के बारे में बताया गया है:

WebWeb
import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const starCountRef = ref(db, 'posts/' + postId + '/starCount');
onValue(starCountRef, (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', (snapshot) => {
  const data = snapshot.val();
  updateStarCount(postElement, data);
});

लिसनर को एक snapshot मिलता है, जिसमें इवेंट के समय डेटाबेस में बताई गई जगह का डेटा होता है. val() तरीके का इस्तेमाल करके, snapshot में डेटा वापस पाया जा सकता है.

डेटा को एक बार पढ़ना

get() की मदद से डेटा को एक बार पढ़ना

एसडीके टूल को डेटाबेस सर्वर के साथ इंटरैक्शन मैनेज करने के लिए डिज़ाइन किया गया है. भले ही, आपका ऐप्लिकेशन ऑनलाइन हो या ऑफ़लाइन.

आम तौर पर, डेटा पढ़ने के लिए, आपको ऊपर बताई गई वैल्यू इवेंट की तकनीकों का इस्तेमाल करना चाहिए. इससे आपको बैकएंड से डेटा में हुए अपडेट की सूचना मिलती है. ऑडियंस को सुनने के लिए इस्तेमाल होने वाली तकनीकों से, आपके डेटा के इस्तेमाल और बिलिंग में कमी आती है. साथ ही, ये तकनीकें ऑप्टिमाइज़ की गई हैं, ताकि आपके उपयोगकर्ताओं को ऑनलाइन और ऑफ़लाइन, दोनों ही स्थितियों में बेहतर अनुभव मिल सके.

अगर आपको डेटा सिर्फ़ एक बार चाहिए, तो डेटाबेस से डेटा का स्नैपशॉट पाने के लिए, get() का इस्तेमाल किया जा सकता है. अगर किसी वजह से get(), सर्वर वैल्यू नहीं दिखा पाता है, तो क्लाइंट लोकल स्टोरेज कैश मेमोरी की जांच करेगा. अगर वैल्यू अब भी नहीं मिलती है, तो वह गड़बड़ी दिखाएगा.

get() का गलत इस्तेमाल करने से बैंडविड्थ का इस्तेमाल बढ़ सकता है और परफ़ॉर्मेंस में गिरावट आ सकती है. हालांकि, ऊपर बताए गए तरीके से रीयल टाइम लिसनर का इस्तेमाल करके, इस समस्या को रोका जा सकता है.

WebWeb
import { getDatabase, ref, child, get } from "firebase/database";

const dbRef = ref(getDatabase());
get(child(dbRef, `users/${userId}`)).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});
const dbRef = firebase.database().ref();
dbRef.child("users").child(userId).get().then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log("No data available");
  }
}).catch((error) => {
  console.error(error);
});

ऑब्ज़र्वर की मदद से डेटा को एक बार पढ़ना

कुछ मामलों में, हो सकता है कि आप सर्वर पर अपडेट की गई वैल्यू की जांच करने के बजाय, स्थानीय कैश मेमोरी में मौजूद वैल्यू तुरंत पाना चाहें. ऐसे मामलों में, once() का इस्तेमाल करके, लोकल डिस्क कैश मेमोरी से तुरंत डेटा पाया जा सकता है.

यह उस डेटा के लिए फ़ायदेमंद है जिसे सिर्फ़ एक बार लोड करना होता है और जिसे बार-बार बदलने की ज़रूरत नहीं होती या जिसे सुनने की ज़रूरत नहीं होती. उदाहरण के लिए, पिछले उदाहरणों में दिया गया ब्लॉगिंग ऐप्लिकेशन, इस तरीके का इस्तेमाल करके उपयोगकर्ता की प्रोफ़ाइल को लोड करता है. ऐसा तब होता है, जब उपयोगकर्ता कोई नई पोस्ट लिखना शुरू करता है:

WebWeb
import { getDatabase, ref, onValue } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const userId = auth.currentUser.uid;
return onValue(ref(db, '/users/' + userId), (snapshot) => {
  const username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
}, {
  onlyOnce: true
});
var userId = firebase.auth().currentUser.uid;
return firebase.database().ref('/users/' + userId).once('value').then((snapshot) => {
  var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
  // ...
});

डेटा अपडेट करना या मिटाना

चुनिंदा फ़ील्ड अपडेट करना

किसी नोड के दूसरे चाइल्ड नोड को ओवरराइट किए बिना, एक साथ कई चाइल्ड नोड में लिखने के लिए, update() तरीके का इस्तेमाल करें.

update() को कॉल करते समय, कुंजी के लिए पाथ तय करके, कम लेवल की चाइल्ड वैल्यू अपडेट की जा सकती हैं. अगर डेटा को बेहतर तरीके से स्केल करने के लिए, एक से ज़्यादा जगहों पर सेव किया गया है, तो डेटा फ़ैन-आउट का इस्तेमाल करके, उस डेटा के सभी इंस्टेंस अपडेट किए जा सकते हैं.

उदाहरण के लिए, कोई सोशल ब्लॉगिंग ऐप्लिकेशन, पोस्ट बना सकता है और साथ ही, इसे हाल ही की गतिविधि वाले फ़ीड और पोस्ट करने वाले उपयोगकर्ता के गतिविधि फ़ीड में अपडेट कर सकता है. इसके लिए, इस तरह के कोड का इस्तेमाल किया जा सकता है:

WebWeb
import { getDatabase, ref, child, push, update } from "firebase/database";

function writeNewPost(uid, username, picture, title, body) {
  const db = getDatabase();

  // A post entry.
  const postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  const newPostKey = push(child(ref(db), 'posts')).key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  const updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return update(ref(db), updates);
}
function writeNewPost(uid, username, picture, title, body) {
  // A post entry.
  var postData = {
    author: username,
    uid: uid,
    body: body,
    title: title,
    starCount: 0,
    authorPic: picture
  };

  // Get a key for a new Post.
  var newPostKey = firebase.database().ref().child('posts').push().key;

  // Write the new post's data simultaneously in the posts list and the user's post list.
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  updates['/user-posts/' + uid + '/' + newPostKey] = postData;

  return firebase.database().ref().update(updates);
}

इस उदाहरण में, push() का इस्तेमाल करके /posts/$postid पर सभी उपयोगकर्ताओं की पोस्ट वाले नोड में एक पोस्ट बनाई गई है. साथ ही, एक ही समय में कुंजी भी वापस पाई गई है. इसके बाद, इस पासकोड का इस्तेमाल करके /user-posts/$userid/$postid पर उपयोगकर्ता की पोस्ट में दूसरी एंट्री बनाई जा सकती है.

इन पाथ का इस्तेमाल करके, update() को एक बार कॉल करके, JSON ट्री में एक से ज़्यादा जगहों पर एक साथ अपडेट किए जा सकते हैं. उदाहरण के लिए, यह उदाहरण दोनों जगहों पर नई पोस्ट कैसे बनाता है. इस तरह से एक साथ किए गए अपडेट ऐटॉमिक होते हैं: या तो सभी अपडेट पूरे हो जाते हैं या सभी अपडेट पूरे नहीं हो पाते.

प्रोसेस पूरी होने पर कॉलबैक जोड़ना

अगर आपको यह जानना है कि आपका डेटा कब कमिट किया गया है, तो आपके पास 'पूरा होने पर कॉलबैक' जोड़ने का विकल्प है. set() और update(), दोनों में एक वैकल्पिक 'पूरा होने पर कॉलबैक' होता है. इसे तब कॉल किया जाता है, जब डेटाबेस में डेटा डालने की प्रोसेस पूरी हो जाती है. अगर कॉल पूरा नहीं हो पाता है, तो कॉलबैक में एक गड़बड़ी ऑब्जेक्ट पास किया जाता है. इससे पता चलता है कि कॉल पूरा न होने की वजह क्या है.

WebWeb
import { getDatabase, ref, set } from "firebase/database";

const db = getDatabase();
set(ref(db, 'users/' + userId), {
  username: name,
  email: email,
  profile_picture : imageUrl
})
.then(() => {
  // Data saved successfully!
})
.catch((error) => {
  // The write failed...
});
firebase.database().ref('users/' + userId).set({
  username: name,
  email: email,
  profile_picture : imageUrl
}, (error) => {
  if (error) {
    // The write failed...
  } else {
    // Data saved successfully!
  }
});

डेटा मिटाना

डेटा मिटाने का सबसे आसान तरीका यह है कि डेटा की जगह के रेफ़रंस पर remove() को कॉल करें.

set() या update() जैसे किसी अन्य लिखने वाले ऑपरेशन के लिए वैल्यू के तौर पर null तय करके भी मिटाया जा सकता है. एक ही एपीआई कॉल में कई बच्चों की जानकारी मिटाने के लिए, update() के साथ इस तकनीक का इस्तेमाल किया जा सकता है.

Promise पाना

Firebase Realtime Database सर्वर पर आपका डेटा कब कमिट किया गया, यह जानने के लिए Promise का इस्तेमाल किया जा सकता है. set() और update(), दोनों Promise दिखा सकते हैं. इसका इस्तेमाल करके, यह पता लगाया जा सकता है कि डेटाबेस में डेटा कब डाला गया.

लिसनर को अलग करना

कॉलबैक हटाने के लिए, अपने Firebase डेटाबेस रेफ़रंस पर off() तरीके को कॉल करें.

किसी एक दर्शक को हटाने के लिए, उसे off() के पैरामीटर के तौर पर पास करें. किसी जगह के लिए off() को बिना किसी आर्ग्युमेंट के कॉल करने पर, उस जगह पर मौजूद सभी दर्शक हट जाते हैं.

किसी पैरंट लिसनर पर off() को कॉल करने से, उसके चाइल्ड नोड पर रजिस्टर किए गए लिसनर अपने-आप नहीं हटते. कॉलबैक हटाने के लिए, चाइल्ड लिसनर पर भी off() को कॉल करना होगा.

डेटा को लेन-देन के तौर पर सेव करना

अगर आपको ऐसे डेटा के साथ काम करना है जो एक साथ होने वाले बदलावों की वजह से खराब हो सकता है, तो लेन-देन के ऑपरेशन का इस्तेमाल करें. जैसे, इंक्रीमेंटल काउंटर. इस ऑपरेशन में अपडेट फ़ंक्शन और ज़रूरी नहीं है कि पूरा होने पर कॉलबैक फ़ंक्शन दिया जाए. अपडेट फ़ंक्शन, डेटा की मौजूदा स्थिति को आर्ग्युमेंट के तौर पर लेता है और आपको जो नई स्थिति लिखनी है उसे दिखाता है. अगर आपकी नई वैल्यू को सेव करने से पहले कोई दूसरा क्लाइंट उस जगह पर डेटा सेव करता है, तो अपडेट फ़ंक्शन को नई मौजूदा वैल्यू के साथ फिर से कॉल किया जाता है और डेटा को सेव करने की कोशिश फिर से की जाती है.

उदाहरण के लिए, सोशल ब्लॉगिंग ऐप्लिकेशन के उदाहरण में, उपयोगकर्ताओं को पोस्ट पर स्टार देने और हटाने की अनुमति दी जा सकती है. साथ ही, इस तरह से यह भी ट्रैक किया जा सकता है कि किसी पोस्ट को कितने स्टार मिले हैं:

WebWeb
import { getDatabase, ref, runTransaction } from "firebase/database";

function toggleStar(uid) {
  const db = getDatabase();
  const postRef = ref(db, '/posts/foo-bar-123');

  runTransaction(postRef, (post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}
function toggleStar(postRef, uid) {
  postRef.transaction((post) => {
    if (post) {
      if (post.stars && post.stars[uid]) {
        post.starCount--;
        post.stars[uid] = null;
      } else {
        post.starCount++;
        if (!post.stars) {
          post.stars = {};
        }
        post.stars[uid] = true;
      }
    }
    return post;
  });
}

ट्रांज़ैक्शन का इस्तेमाल करने से, स्टार की गिनती गलत होने से बचती है. ऐसा तब होता है, जब एक ही समय पर कई उपयोगकर्ता एक ही पोस्ट को स्टार करते हैं या क्लाइंट के पास पुराना डेटा होता है. अगर लेन-देन अस्वीकार कर दिया जाता है, तो सर्वर क्लाइंट को मौजूदा वैल्यू दिखाता है. इसके बाद, क्लाइंट अपडेट की गई वैल्यू के साथ लेन-देन फिर से करता है. यह तब तक दोहराया जाता है, जब तक लेन-देन स्वीकार नहीं हो जाता या आप लेन-देन को रद्द नहीं कर देते.

सर्वर साइड पर एक साथ कई बदलाव करना

ऊपर दिए गए इस्तेमाल के उदाहरण में, हम डेटाबेस में दो वैल्यू लिख रहे हैं: पोस्ट को स्टार देने/अनस्टार करने वाले उपयोगकर्ता का आईडी और स्टार की संख्या में हुई बढ़ोतरी. अगर हमें पहले से पता है कि उपयोगकर्ता पोस्ट को स्टार कर रहा है, तो हम लेन-देन के बजाय, एटमिक इंक्रीमेंट ऑपरेशन का इस्तेमाल कर सकते हैं.

WebWeb
function addStar(uid, key) {
  import { getDatabase, increment, ref, update } from "firebase/database";
  const dbRef = ref(getDatabase());

  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = increment(1);
  update(dbRef, updates);
}
function addStar(uid, key) {
  const updates = {};
  updates[`posts/${key}/stars/${uid}`] = true;
  updates[`posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  updates[`user-posts/${key}/stars/${uid}`] = true;
  updates[`user-posts/${key}/starCount`] = firebase.database.ServerValue.increment(1);
  firebase.database().ref().update(updates);
}

यह कोड, लेन-देन के ऑपरेशन का इस्तेमाल नहीं करता. इसलिए, अगर कोई अपडेट करने में कोई समस्या आती है, तो यह कोड अपने-आप फिर से नहीं चलता. हालांकि, डेटाबेस सर्वर पर सीधे तौर पर इंक्रीमेंट ऑपरेशन होने की वजह से, डेटा में कोई गड़बड़ी नहीं होती.

अगर आपको ऐप्लिकेशन से जुड़ी समस्याओं का पता लगाना है और उन्हें अस्वीकार करना है, तो आपको उस इस्तेमाल के उदाहरण के लिए, सुरक्षा से जुड़े कस्टम नियम लिखने चाहिए. उदाहरण के लिए, जब कोई उपयोगकर्ता किसी ऐसी पोस्ट को स्टार कर दे जिसे उसने पहले ही स्टार कर दिया है.

डेटा के साथ ऑफ़लाइन काम करना

अगर किसी क्लाइंट का नेटवर्क कनेक्शन बंद हो जाता है, तो भी आपका ऐप्लिकेशन ठीक से काम करता रहेगा.

Firebase डेटाबेस से कनेक्ट किया गया हर क्लाइंट, किसी भी ऐक्टिव डेटा का अपना इंटरनल वर्शन मैनेज करता है. डेटा लिखने पर, वह सबसे पहले इस लोकल वर्शन में लिखा जाता है. इसके बाद, Firebase क्लाइंट उस डेटा को रिमोट डेटाबेस के सर्वर और अन्य क्लाइंट के साथ "बेहतरीन तरीके" से सिंक करता है.

इस वजह से, डेटाबेस में किए गए सभी बदलाव, सर्वर में डेटा सेव होने से पहले ही लोकल इवेंट को तुरंत ट्रिगर कर देते हैं. इसका मतलब है कि नेटवर्क के इंतज़ार या कनेक्टिविटी के बावजूद, आपका ऐप्लिकेशन काम करता रहेगा.

कनेक्शन फिर से चालू होने के बाद, आपके ऐप्लिकेशन को इवेंट का सही सेट मिलता है, ताकि क्लाइंट को कोई कस्टम कोड लिखे बिना, सर्वर की मौजूदा स्थिति के साथ सिंक किया जा सके.

हम ऑनलाइन और ऑफ़लाइन सुविधाओं के बारे में ज़्यादा जानें लेख में, ऑफ़लाइन व्यवहार के बारे में ज़्यादा बात करेंगे.

अगले चरण