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

(ज़रूरी नहीं) 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 के इंस्टेंस की ज़रूरत होगी:

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

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

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

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

अगले चरण