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

(ज़रूरी नहीं) 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 प्लैटफ़ॉर्म के एसडीके का इस्तेमाल करके, अपने ऐप्लिकेशन के प्रोटोटाइप कोड से कॉल करना. इसके अलावा, Realtime Database REST API का इस्तेमाल करना.Realtime DatabaseRealtime Database

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

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

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

Web

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web

var database = firebase.database();

डेटा लिखना

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

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

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

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

Web

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
  });
}

Web

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 दिखाता है.

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

Web

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);
});

Web

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() का ज़रूरत से ज़्यादा इस्तेमाल करने से, बैंडविथ का इस्तेमाल बढ़ सकता है और परफ़ॉर्मेंस में कमी आ सकती है. इसे ऊपर दिखाए गए रीयलटाइम लिसनर का इस्तेमाल करके रोका जा सकता है.

Web

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);
});

Web

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() का इस्तेमाल किया जा सकता है.

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

Web

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
});

Web

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

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

Web

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);
}

Web

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 पर दूसरी एंट्री बनाई जा सकती है.

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

पूरा होने पर मिलने वाला कॉलबैक जोड़ना

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

Web

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...
});

Web

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() को कॉल करना ज़रूरी है.

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

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

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

Web

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;
  });
}

Web

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;
  });
}

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

सर्वर-साइड पर ऐटॉमिक तरीके से बढ़ोतरी करना

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

Web

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);
}

Web

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 क्लाइंट, उस डेटा को रिमोट डेटाबेस सर्वर और अन्य क्लाइंट के साथ "सबसे सही कोशिश" के आधार पर सिंक करता है.

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

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

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

अगले चरण