Cloud Functions-এর মাধ্যমে, আপনি ক্লায়েন্ট কোড আপডেট না করেই Firebase Realtime Database-এ ইভেন্ট ম্যানেজ করতে পারবেন। Cloud Functions আপনাকে সম্পূর্ণ অ্যাডমিনিস্ট্রেটিভ বিশেষ সুবিধা সহ Realtime Database অপারেশন চালাতে দেয় এবং নিশ্চিত করে যে Realtime Database-এ প্রতিটি পরিবর্তন আলাদাভাবে প্রসেস করা হয়। আপনি ডেটা স্ন্যাপশট বা অ্যাডমিন SDK-এর মাধ্যমে Firebase Realtime Databaseটি পরিবর্তন করতে পারবেন।
একটি সাধারণ লাইফসাইকেলে, Firebase Realtime Database ফাংশন নিম্নলিখিত কাজগুলি করে:
- নির্দিষ্ট Realtime Database পাথে পরিবর্তন হওয়ার জন্য অপেক্ষা করে।
- কোনও ইভেন্ট ঘটলে ট্রিগার হয় এবং তার কাজ করে।
- একটি ডেটা অবজেক্ট পায় যাতে সেই পাথে স্টোর করা ডেটার স্ন্যাপশট থাকে।
Firebase Realtime Database-এ ডেটাবেস নোড লেখা, তৈরি করা, আপডেট করা বা মুছে ফেলার প্রতিক্রিয়ায় আপনি কোনও ফাংশন ট্রিগার করতে পারেন। ফাংশন কখন ট্রিগার হবে তা নিয়ন্ত্রণ করতে, ইভেন্ট হ্যান্ডলারগুলির মধ্যে একটি নির্দিষ্ট করুন এবং Realtime Database পাথ নির্দিষ্ট করুন যেখানে এটি ইভেন্টের জন্য শুনবে।
ফাংশনের লোকেশন সেট করুন
Realtime Database ইনস্ট্যান্সের লোকেশন এবং ফাংশনের লোকেশনের মধ্যে দূরত্বের কারণে নেটওয়ার্ক লেটেন্সি উল্লেখযোগ্যভাবে বেড়ে যেতে পারে। এছাড়াও, অঞ্চলগুলির মধ্যে অমিল থাকলে ডিপ্লয়মেন্ট ব্যর্থ হতে পারে। এই পরিস্থিতি এড়াতে, ফাংশন লোকেশন নির্দিষ্ট করুন যাতে এটি ডেটাবেস ইনস্ট্যান্স লোকেশন-এর সাথে মিলে যায়।
Realtime Databaseটি ইভেন্ট ম্যানেজ করুন
ফাংশন আপনাকে দুটি নির্দিষ্ট লেভেলে Realtime Database ইভেন্ট ম্যানেজ করতে দেয়; আপনি শুধুমাত্র লেখা, তৈরি করা, আপডেট করা বা মুছে ফেলার ইভেন্ট শুনতে পারেন অথবা কোনও রেফারেন্সে যেকোনও ধরনের পরিবর্তন শুনতে পারেন।
Realtime Database ইভেন্টের উত্তর দেওয়ার জন্য এইসব হ্যান্ডলার উপলভ্য:
Node.js
onValueWritten()Realtime Database-এ ডেটা তৈরি, আপডেট বা মুছে ফেলা হলে ট্রিগার করা হয়।onValueCreated()Realtime Database-এ ডেটা তৈরি হলে তবেই ট্রিগার করা হয়।onValueUpdated()Realtime Database-এ ডেটা আপডেট করা হলে তবেই ট্রিগার করা হয়।onValueDeleted()শুধুমাত্র Realtime Database-এ ডেটা মুছে ফেলা হলে ট্রিগার করা হয়।
Python
on_value_written()Realtime Database-এ ডেটা তৈরি, আপডেট বা মুছে ফেলা হলে ট্রিগার করা হয়।on_value_created()Realtime Database-এ ডেটা তৈরি হলে তবেই ট্রিগার করা হয়।on_value_updated()Realtime Database-এ ডেটা আপডেট করা হলে তবেই ট্রিগার করা হয়।on_value_deleted()শুধুমাত্র Realtime Database-এ ডেটা মুছে ফেলা হলে ট্রিগার করা হয়।
প্রয়োজনীয় মডিউল ইমপোর্ট করুন
আপনার ফাংশন সোর্সে, আপনি যে SDK মডিউল ব্যবহার করতে চান তা ইমপোর্ট করতে হবে। এই স্যাম্পেলের জন্য, Realtime Database-এ লেখার জন্য Firebase Admin SDK মডিউলের সাথে HTTP এবং Realtime Database মডিউল ইমপোর্ট করা প্রয়োজন।
Node.js
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/https");
const {onValueCreated} = require("firebase-functions/database");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
Python
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn
# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db
app = initialize_app()
ইনস্ট্যান্স ও পাথ উল্লেখ করা
আপনার ফাংশন কখন এবং কোথায় ট্রিগার করা উচিত তা নিয়ন্ত্রণ করতে, একটি পাথ এবং ঐচ্ছিকভাবে একটি Realtime Database ইনস্ট্যান্স সহ আপনার ফাংশন কনফিগার করুন। আপনি কোনও ইনস্ট্যান্স নির্দিষ্ট না করলে, ফাংশনটি ফাংশন অঞ্চলের সমস্ত Realtime Database ইনস্ট্যান্স শোনে। একই অঞ্চলের ইনস্ট্যান্সের একটি নির্বাচনী সাবসেটে ডিপ্লয় করার জন্য আপনি একটি Realtime Database ইনস্ট্যান্স প্যাটার্নও নির্দিষ্ট করতে পারেন।
যেমন:
Node.js
// All Realtime Database instances at path "/user/{uid}" // There must be at least one Realtime Database present. const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => { // … }); // Instance named "my-app-db-2", at path "/user/{uid}". // The "my-app-db-2" instance must exist in this region. const OnWrittenFunctionInstance = onValueWritten( { ref: "/user/{uid}", instance: "my-app-db-2" }, (event) => { // … } ); // Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com. // There must be at least one Realtime Database with "my-app-db-*" prefix in this region. const onWrittenFunctionInstance = onValueWritten( { ref: "/user/{uid=*@gmail.com}", instance: "my-app-db-*" }, (event) => { // … } );
Python
# All Realtime Database instances at path "/user/{uid}"
# There must be at least one Realtime Database present.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
reference=r"/user/{uid}",
instance="my-app-db-2",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
reference=r"/user/{uid=*@gmail.com}",
instance="my-app-db-*",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
এইসব প্যারামিটার আপনার ফাংশনকে Realtime Database ইনস্ট্যান্সের মধ্যে নির্দিষ্ট পাথে রাইট হ্যান্ডেল করার নির্দেশ দেয়।
পাথ স্পেসিফিকেশন সব রাইটের সাথে ম্যাচ করে যা একটি পাথকে স্পর্শ করে, যার মধ্যে এর নিচে যেকোনও জায়গায় হওয়া রাইট অন্তর্ভুক্ত। আপনি যদি ফাংশনের জন্য
পাথ /foo/bar হিসেবে সেট করেন, তাহলে এটি এই দুটি লোকেশনেই ইভেন্টের সাথে মিলে যায়:
/foo/bar
/foo/bar/baz/really/deep/path
দুটি ক্ষেত্রেই, Firebase ধরে নেয় যে ইভেন্টটি /foo/bar-এ ঘটেছে,
এবং ইভেন্ট ডেটাতে /foo/bar-এর পুরনো এবং নতুন ডেটা অন্তর্ভুক্ত থাকে। ইভেন্ট ডেটা
বড় হলে, আপনার ডেটাবেসের রুটের কাছাকাছি একটি ফাংশন ব্যবহার করার পরিবর্তে
আরও গভীর পাথে একাধিক ফাংশন ব্যবহার করার কথা বিবেচনা করুন। সেরা পারফর্ম্যান্সের জন্য,
শুধুমাত্র যতটা সম্ভব গভীর লেভেলে ডেটার অনুরোধ করুন।
ওয়াইল্ডকার্ড ও ক্যাপচার
ক্যাপচার করার জন্য আপনি {key}, {key=*}, {key=prefix*}, {key=*suffix}
ব্যবহার করতে পারবেন। *, prefix*, *suffix সিঙ্গেল-সেগমেন্ট ওয়াইল্ডকার্ডিংয়ের জন্য।
মনে রাখবেন: ** মাল্টি-সেগমেন্ট ওয়াইল্ডকার্ডিংকে দেখায়, যা Realtime Database-এ কাজ করে না।
পাথ প্যাটার্ন বোঝা দেখুন।
পাথ ওয়াইল্ডকার্ডিং। আপনি ওয়াইল্ডকার্ড হিসেবে পাথ কম্পোনেন্ট নির্দিষ্ট করতে পারেন:
- অ্যাস্টেরিস্ক ব্যবহার করে,
*। যেমন,foo/*foo/-এর নিচে নোড হায়ারার্কির এক লেভেল নিচে থাকা যেকোনও চাইল্ডের সাথে ম্যাচ করে। - অ্যাস্টেরিস্ক,
*সহ সেগমেন্ট ব্যবহার করা। যেমন,foo/app*-usfoo/-এর নিচেappপ্রিফিক্স ও-usসাফিক্স সহ যেকোনও চাইল্ড সেগমেন্টের সাথে ম্যাচ করে।
ওয়াইল্ডকার্ড সহ পাথ একাধিক ইভেন্টের সাথে ম্যাচ করতে পারে, যেমন, একটি রাইট। এর একটি ইনসার্ট
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
"/foo/*" পাথের সাথে দুবার ম্যাচ করে: একবার "hello": "world"-এর সাথে এবং আবার
"firebase": "functions"-এর সাথে।
পাথ ক্যাপচার করা। আপনার ফাংশন কোডে (যেমন, /user/{uid}, /user/{uid=*-us}) ব্যবহার করার জন্য আপনি নামযুক্ত ভেরিয়েবলে
পাথ ম্যাচ ক্যাপচার করতে পারবেন।
ক্যাপচার ভেরিয়েবলের ভ্যালু আপনার ফাংশনের database.DatabaseEvent.params অবজেক্টের মধ্যে উপলভ্য।
ইনস্ট্যান্স ওয়াইল্ডকার্ডিং। এছাড়াও, আপনি ওয়াইল্ডকার্ডিং ব্যবহার করে ইনস্ট্যান্স
কম্পোনেন্ট নির্দিষ্ট করতে পারবেন। ইন্সট্যান্স ওয়াইল্ডকার্ডে প্রিফিক্স, সাফিক্স বা দুটিই থাকতে পারে
(যেমন, my-app-*-prod)।
ওয়াইল্ডকার্ড এবং ক্যাপচার রেফারেন্স
Cloud Functions (২য় জেনারেশন) এবং Realtime Database-এর সাথে, ref এবং instance নির্দিষ্ট করার সময়
প্যাটার্ন ব্যবহার করা যেতে পারে। প্রতিটি ট্রিগার ইন্টারফেসে ফাংশন স্কোপ করার জন্য নিম্নলিখিত
বিকল্পগুলি থাকবে:
ref উল্লেখ করা |
instance উল্লেখ করা |
আচরণ |
|---|---|---|
সিঙ্গেল (/foo/bar) |
নির্দিষ্ট করে উল্লেখ করা নেই | ফাংশন অঞ্চলে সব ইনস্ট্যান্সে স্কোপ হ্যান্ডলার। |
সিঙ্গেল (/foo/bar) |
সিঙ্গেল (‘my-new-db') |
ফাংশন অঞ্চলে নির্দিষ্ট ইনস্ট্যান্সের জন্য স্কোপ হ্যান্ডলার। |
সিঙ্গেল (/foo/bar) |
প্যাটার্ন (‘inst-prefix*') |
ফাংশন অঞ্চলে প্যাটার্নের সাথে মেলে এমন সব ইনস্ট্যান্সের জন্য স্কোপ হ্যান্ডলার। |
প্যাটার্ন (/foo/{bar}) |
নির্দিষ্ট করে উল্লেখ করা নেই | ফাংশন অঞ্চলে সব ইনস্ট্যান্সে স্কোপ হ্যান্ডলার। |
প্যাটার্ন (/foo/{bar}) |
সিঙ্গেল (‘my-new-db') |
ফাংশন অঞ্চলে নির্দিষ্ট ইনস্ট্যান্সের জন্য স্কোপ হ্যান্ডলার। |
প্যাটার্ন (/foo/{bar}) |
প্যাটার্ন (‘inst-prefix*') |
ফাংশন অঞ্চলে প্যাটার্নের সাথে মেলে এমন সব ইনস্ট্যান্সের জন্য স্কোপ হ্যান্ডলার। |
ইভেন্ট ডেটা ম্যানেজ করা
Realtime Database ইভেন্ট ট্রিগার হলে, এটি আপনার হ্যান্ডলার ফাংশনে Event অবজেক্ট পাস করে।
এই অবজেক্টে একটি data প্রপার্টি আছে, যা তৈরি ও মুছে দেওয়ার ইভেন্টের জন্য,
তৈরি বা মুছে দেওয়া ডেটার স্ন্যাপশট ধারণ করে।
এই উদাহরণে, ফাংশনটি রেফারেন্স করা পাথের জন্য ডেটা ফিরিয়ে আনে, সেই লোকেশনে স্ট্রিংটিকে আপারকেসে কনভার্ট করে এবং সেই পরিবর্তিত স্ট্রিংটি ডেটাবেসে লেখে:
Node.js
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
"/messages/{pushId}/original",
(event) => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing
// asynchronous tasks inside a function, such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the
// Realtime Database returns a Promise.
return event.data.ref.parent.child("uppercase").set(uppercase);
},
);
Python
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Grab the value that was written to the Realtime Database.
original = event.data
if not isinstance(original, str):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)
আগের মান পড়ুন
write বা update ইভেন্টের জন্য, data প্রপার্টি
হল একটি Change অবজেক্ট যাতে দুটি স্ন্যাপশট থাকে
যা ট্রিগার করা ইভেন্টের আগে এবং পরে ডেটা স্টেটকে দেখায়।
Change অবজেক্টে before প্রপার্টি আছে যা আপনাকে ইভেন্টের আগে Realtime Database-এ কী সেভ করা হয়েছিল তা পরীক্ষা করতে দেয় এবং
after প্রপার্টি ইভেন্ট হওয়ার পরে ডেটার অবস্থা দেখায়।
যেমন, before প্রপার্টি ব্যবহার করে নিশ্চিত করা যায় যে ফাংশনটি শুধুমাত্র
প্রথমবার তৈরি করার সময় টেক্সট বড় হাতের অক্ষরে লিখবে:
Node.js
exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => { // Only edit data when it is first created. if (event.data.before.exists()) { return null; } // Exit when the data is deleted. if (!event.data.after.exists()) { return null; } // Grab the current value of what was written to the Realtime Database. const original = event.data.after.val(); console.log('Uppercasing', event.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return event.data.after.ref.parent.child('uppercase').set(uppercase); });
Python
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Only edit data when it is first created.
if event.data.before is not None:
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value that was written to the Realtime Database.
original = event.data.after
if not hasattr(original, "upper"):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)
যাচাইকরণের প্রসঙ্গ অ্যাক্সেস করুন
RTDB eventarc ইভেন্টের মাধ্যমে ট্রিগার করা ফাংশনের জন্য, যাচাইকরণের প্রসঙ্গ ইভেন্ট পেলোডে অন্তর্ভুক্ত করা হয়:
- authtype: ইভেন্ট ট্রিগার করা প্রিন্সিপালের ধরন। সম্ভাব্য ভ্যালুগুলি হল:
app_user: ডেভেলপারের অ্যাপ্লিকেশনের একজন ব্যবহারকারী।admin: একটি পরিষেবা অ্যাকাউন্ট।unauthenticated: একজন যাচাই না করা ব্যবহারকারী।unknown: যাচাইকরণ সংক্রান্ত তথ্য উপলভ্য না থাকলে ডিফল্ট।
- authid: প্রিন্সিপালের অনন্য শনাক্তকারী।
authtypeযদিapp_userহয়, তাহলে এটি ব্যবহারকারীর UID।authtypeযদিadminহয়, তাহলে এটি পরিষেবা অ্যাকাউন্ট বা IAM ব্যবহারকারীর ইমেল।
এই কোডটি শুধুমাত্র মেসেজের টেক্সটকে বড় হাতের অক্ষরে পরিবর্তন করে যদি ব্যবহারকারী যিনি ফাংশনটি ট্রিগার করেছেন তিনি একজন অ্যাডমিনিস্ট্রেটর নন। এছাড়াও, যে ব্যবহারকারী মেসেজটি ট্রিগার করেছেন তিনি মেসেজের প্রকৃত প্রেরক কিনা তাও কোডটি চেক করে।
Node.js
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onValueWritten} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.dbtrigger = onValueWritten("/messages/{pushId}/original", async (event) => {
// 1. Check whether authtype is admin. If it is, skip this operation.
if (event.authType === "admin") {
logger.log("Modification by admin detected. Skipping uppercase conversion.");
return null;
}
// 2. Retrieve the userID of the sender (assumed sibling node 'senderId')
const snapshot = await event.data.after.ref.parent.child("senderId").get();
const senderId = snapshot.val();
// 3. Check if userID of sender of message = event.authid
if (senderId !== event.authId) {
logger.error(`Unauthorized write: senderId (${senderId}) does not match authId (${event.authId})`);
return null;
}
// Grab the value that was written to the Realtime Database.
const original = event.data.after.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// Return the promise to set the "uppercase" sibling node.
return event.data.after.ref.parent.child("uppercase").set(uppercase);
});
Python
from firebase_functions import db_fn
from firebase_admin import initialize_app, db
initialize_app()
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[db_fn.Change]) -> None:
# 1. Check whether authtype is admin. If it is, skip this operation.
if event.auth_type == "admin":
print("Admin user detected. Skipping.")
return
# 2. Retrieve the userID of the sender (assumed sibling node: 'senderId')
parent_ref = db.reference(event.reference).parent
sender_id = parent_ref.child("senderId").get()
# 3. Check if userID of sender = event.auth_id
if sender_id != event.auth_id:
print(f"Unauthorized: sender_id {sender_id} != auth_id {event.auth_id}")
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value and uppercase it
original = event.data.after
if not isinstance(original, str):
return
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent_ref.child("uppercase").set(upper)