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

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

Add a Completion Callback

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

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

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

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

अगले चरण