ওয়েবে ডেটা পড়া ও লেখা

(ঐচ্ছিক) 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.database.Reference-এ অ্যাসিঙ্ক্রোনাস লিসেনার অ্যাটাচ করে Firebase ডেটা ফিরিয়ে আনা হয়। ডেটার প্রাথমিক অবস্থার জন্য একবার এবং ডেটা পরিবর্তন হলে আবার যেকোনও সময় লিসেনার ট্রিগার হয়।

বেসিক রাইট অপারেশন

বেসিক রাইট অপারেশনের জন্য, আপনি 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() ব্যবহার করে একবার ডেটা পড়া

আপনার অ্যাপ অনলাইন বা অফলাইন যাই হোক না কেন, ডেটাবেস সার্ভারের সাথে ইন্টার‍্যাকশন ম্যানেজ করার জন্য SDK ডিজাইন করা হয়েছে।

সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেট সম্পর্কে বিজ্ঞপ্তি পেতে, ডেটা পড়ার জন্য উপরে বর্ণিত ভ্যালু ইভেন্ট টেকনিক ব্যবহার করা উচিত। লিসনার টেকনিক আপনার ব্যবহার ও বিলিং কম করে এবং ব্যবহারকারী অনলাইন ও অফলাইন হলে তাকে সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে।

আপনার যদি ডেটা শুধুমাত্র একবার প্রয়োজন হয়, তাহলে আপনি 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-এ ব্যবহারকারীর পোস্টে দ্বিতীয় এন্ট্রি তৈরি করতে কী ব্যবহার করা যেতে পারে।

এইসব পাথ ব্যবহার করে, আপনি update()-এ একটি কল করার মাধ্যমে JSON ট্রিতে একাধিক লোকেশনে একসাথে আপডেট করতে পারবেন, যেমন এই উদাহরণে দুটি লোকেশনেই নতুন পোস্ট তৈরি করা হয়েছে। এইভাবে করা যুগপৎ আপডেটগুলি অ্যাটমিক: হয় সব আপডেট সফল হয় অথবা সব আপডেট ব্যর্থ হয়।

সম্পূর্ণ করার কলব্যাক যোগ করুন

আপনার ডেটা কখন কমিট করা হয়েছে তা জানতে চাইলে, আপনি একটি কমপ্লিশন কলব্যাক যোগ করতে পারেন। 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()-এর সাথে ব্যবহার করে একটি API কলের মাধ্যমে একাধিক চাইল্ড মুছে ফেলতে পারবেন।

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 ক্লায়েন্ট তখন রিমোট ডেটাবেস সার্ভার এবং অন্যান্য ক্লায়েন্টের সাথে "সর্বোচ্চ চেষ্টা"র ভিত্তিতে সেই ডেটা সিঙ্ক করে।

এর ফলে, ডেটাবেসে লেখা সমস্ত ডেটা সার্ভারে লেখার আগেই অবিলম্বে স্থানীয় ইভেন্ট ট্রিগার করে। এর অর্থ হল, নেটওয়ার্ক লেটেন্সি বা কানেক্টিভিটি যাই হোক না কেন, আপনার অ্যাপ রেসপন্সিভ থাকে।

কানেক্টিভিটি আবার চালু হয়ে গেলে, আপনার অ্যাপ উপযুক্ত সেট অফ ইভেন্ট পায় যাতে ক্লায়েন্ট বর্তমান সার্ভার স্ট্যাটাসের সাথে সিঙ্ক করে, কোনও কাস্টম কোড না লিখেই।

আমরা অফলাইন আচরণ সম্পর্কে আরও কথা বলব অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও জানুন..

পরবর্তী ধাপ