فائر اسٹور ڈیٹا کو کس طرح منظم کرتا ہے اور اسے CRUD آپریشنز کرنے کے لیے کیسے استعمال کیا جاتا ہے۔

زیادہ تر ایپس کو آخر کار ڈیٹا کو اسٹور اور ہیرا پھیری کرنے کی ضرورت ہوتی ہے۔ جب آپ Firebase کے ساتھ بناتے ہیں، تو آپ کا ڈیٹا Firestore، Google کے لچکدار اور قابل توسیع NoSQL دستاویز ڈیٹا بیس میں محفوظ ہوتا ہے۔

لیکن اس سے پہلے کہ آپ اعتماد کے ساتھ ڈیٹا کو تخلیق، پڑھ، اپ ڈیٹ اور حذف کر سکیں، آپ کو یہ سمجھنا ہوگا کہ Firestore دراصل معلومات کو کس طرح منظم کرتا ہے۔ یہ ایس کیو ایل ڈیٹا بیس کی طرح نظر نہیں آتا، اور اس کو ایس کیو ایل ڈیٹا بیس کی طرح برتاؤ گندا، مشکل سے استفسار کرنے والے ڈیٹا ڈھانچے کو ختم کرنے کا تیز ترین طریقہ ہے۔

اس ٹیوٹوریل میں، آپ سیکھیں گے کہ Firestore کا NoSQL ڈیٹا ماڈل کیسے کام کرتا ہے، اور پھر Firebase Web SDK (v9+، ماڈیولر) کا استعمال کرتے ہوئے تمام CRUD آپریشنز کی مشق کرنے کے لیے ایک چھوٹی ٹاسک مینجمنٹ ایپ بنائیں۔ آخر میں، آپ آپریشنز، استفسار، نیسٹڈ فیلڈز اور صفوں کو اپ ڈیٹ کر سکتے ہیں، اور غیر منسلک ذیلی جمعوں کو چھوڑے بغیر ڈیٹا کو محفوظ طریقے سے حذف کر سکتے ہیں۔

انڈیکس

شرطیں

شروع کرنے سے پہلے، یقینی بنائیں کہ آپ کے پاس درج ذیل ہیں:

  • Node.js v18 یا اس سے زیادہ (node --version)

  • کوئی راستہ نہیں گوگل اکاؤنٹ ایک Firebase پروجیکٹ بنائیں (مفت اسپارک پلان اس ٹیوٹوریل کے لیے کافی ہے)

  • جاوا اسکرپٹ کا بنیادی علم، بشمول: async/await اور ES ماڈیول

  • کوڈ ایڈیٹر اور ٹرمینل

یہ گائیڈ آپ کو بنیادی باتوں سے لیتا ہے، لہذا آپ کو Firebase یا NoSQL ڈیٹا بیس کے ساتھ کسی پیشگی تجربے کی ضرورت نہیں ہے۔

Firestore ڈیٹا کی ساخت کیسے بناتا ہے۔

اگر آپ رشتہ دار (SQL) پس منظر سے آتے ہیں، تو آپ کو سب سے پہلی چیز جو سیکھنے کی ضرورت ہے وہ ہے فکسڈ اسکیموں اور غیر ملکی کلیدوں کے ساتھ جدولوں کا خیال۔ فائر اسٹور دستاویز پر مبنی NoSQL ڈیٹا بیسہم اپنے ڈیٹا کو دو اہم تصورات کے گرد ترتیب دیتے ہیں: مجموعہ اور دستاویز.

  • مجموعہ ایک نامزد بالٹی ہے جس میں دستاویزات ہوتی ہیں۔ سوچو tasks، usersیا orders.

  • ایک دستاویز ایک مجموعہ کے اندر ایک واحد ریکارڈ ہے جس کی شناخت ایک منفرد ID سے ہوتی ہے۔ JSON آبجیکٹ کی طرح، یہ ڈیٹا کو کلیدی قدر کے جوڑوں کے طور پر اسٹور کرتا ہے۔

یہاں ایک مسئلہ ہے جو بہت سے نئے آنے والوں کو حیران کر دیتا ہے: دستاویزات کو ایک جیسے فیلڈز کا اشتراک کرنے کی ضرورت نہیں ہے۔. ایک task دستاویز ہے۔ dueDate دوسرے فیلڈز ایسا نہیں کرتے۔ فائر اسٹور ڈیٹا بیس کی سطح پر اسکیما کو نافذ نہیں کرتا ہے، اس ذمہ داری کو ایپلیکیشن کوڈ پر منتقل کرتا ہے۔

نیسٹڈ اور ذیلی مجموعے۔

ایک دستاویز میں دو قسم کے نیسٹڈ ڈیٹا پر مشتمل ہو سکتا ہے:

  • نقشہایک شے ہے جو براہ راست کسی دستاویز کے اندر اندر کی گئی ہے، جیسے metadata پر مشتمل فیلڈز: { priority, dueDate })

  • ذیلی مجموعہیہ ایک مخصوص دستاویز کے تحت اندر اندر ایک مکمل مجموعہ ہے (مثال کے طور پر، ہر کام کی اپنی دستاویز ہو سکتی ہے)۔ comments ذیلی مجموعے)

یہ آپ کو ایک ایسا ڈھانچہ دے گا جو درخت کی طرح نظر آتا ہے۔

tasks (collection)
 └── taskId (document)
      ├── title: "Article title"
      ├── completed: false
      ├── tags: ["writing", "firebase"]
      ├── metadata: { priority: "high", dueDate:  }
      └── comments (subcollection)
           └── commentId (document)
                ├── text: "CRUD Article"
                └── createdAt: 

تائید شدہ ڈیٹا کی اقسام

فائر اسٹور دستاویزات کئی قدیم اقسام کو محفوظ کر سکتے ہیں۔ جن کو آپ اکثر استعمال کریں گے وہ ہیں:

زمرہ ہاں
string "Write CRUD article"
number 42
boolean true
array ["writing", "firebase"]
map { priority: "high" }
timestamp Timestamp.now()
reference کسی اور دستاویز کی طرف اشارہ کریں۔
geopoint عرض البلد/طول البلد جوڑا

CRUD کوڈ لکھنے سے پہلے یہ کیوں ضروری ہے۔

تمام CRUD آپریشنز جو آپ بعد میں لکھیں گے درج ذیل ڈھانچے پر منحصر ہوں گے:

  • بنانا اس کا مطلب ہے کہ خود کار طریقے سے تیار کردہ ID یا حسب ضرورت ID کا استعمال کرتے ہوئے کسی مجموعہ میں دستاویزات شامل کرنا۔

  • پڑھیں اس کا مطلب ہے کہ کسی ایک دستاویز کو ID کے ذریعے بازیافت کرنا یا کسی سوال سے مماثل دستاویزات کا مجموعہ۔

  • اپ ڈیٹ اس کا مطلب ہے کسی موجودہ دستاویز میں فیلڈز میں ترمیم کرنا، بشمول نیسٹڈ میپس اور ارے۔

  • حذف کریں مطلب دستاویز اور فائر اسٹور کو حذف کرنا ~ نہیں ذیلی مجموعہ کو خود بخود صاف کریں (مرحلہ 6 میں دیکھا گیا ایک عام مسئلہ)۔

اب جب کہ ہمارے پاس اپنا ذہنی ماڈل تیار ہے، آئیے اپنا پروجیکٹ ترتیب دیں اور کوڈ لکھنا شروع کریں۔

مرحلہ 1 – اپنا Firebase پروجیکٹ ترتیب دیں۔

Firebase کنسول پر جائیں اور ایک نیا پروجیکٹ بنائیں۔

  1. کلک کریں پروجیکٹ شامل کریں۔اسے ایک نام دیں، جیسے crud-tasks-demo)، سیٹ اپ وزرڈ کی پیروی کریں (Google Analytics اس ٹیوٹوریل میں اختیاری ہے)۔

  2. پروجیکٹ بننے کے بعد، بائیں سائڈبار کھولیں اور ڈیٹا بیس اور اسٹوریج پھر فائر اسٹیشن.

  3. کلک کریں ڈیٹا بیس بنائیں. اپنے قریب ایک مقام کا انتخاب کریں اور اس ٹیوٹوریل کو یہاں سے شروع کریں: ٹیسٹ موڈ آپ ابھی تک کسی بھی حفاظتی اصول کو ترتیب دینے کے بغیر پڑھ اور لکھ سکتے ہیں۔

میمو: ٹیسٹ موڈ میں، ڈیٹا بیس 30 دنوں کے لیے عوام کے لیے کھلا رہتا ہے۔ مناسب Firestore حفاظتی اصولوں کے بغیر اپنی ایپ کو پروڈکشن میں جاری نہ کریں۔ ہم ڈیبگنگ سیکشن میں اس کا احاطہ کریں گے۔

اب آپ اپنا پہلا مجموعہ وصول کرنے کے لیے تیار ایک خالی Firestore ڈیٹا بیس دیکھیں گے۔

مرحلہ 2 – SDK شروع کریں۔

ایک نیا پروجیکٹ فولڈر بنائیں اور Firebase ویب SDK انسٹال کریں۔

mkdir firestore-crud-demo && cd firestore-crud-demo
npm init -y
npm install firebase

پروجیکٹ کی کنفیگریشن آبجیکٹ اس سے حاصل کریں: پروجیکٹ کی ترتیبات – عمومی – ایپ – ویب ایپ Firebase کنسول میں (ایک نئی ویب ایپ رجسٹر کریں اگر آپ کے پاس پہلے سے نہیں ہے)

بنانا firebase-config.js فائل:

// firebase-config.js
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_PROJECT_ID.appspot.com",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID",
};

const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);

CRUD کی تمام مثالیں یہاں حاصل کریں۔ db اس فائل میں۔ اپنی اصل ترتیب کی اقدار کو ورژن کنٹرول میں رکھیں (حقیقی پروجیکٹس میں، ماحولیاتی متغیرات کا استعمال کریں)۔

مرحلہ 3 – تخلیق کریں: ٹاسک شامل کریں۔

Firestore ایک دستاویز بنانے کے دو طریقے فراہم کرتا ہے: Firestore سے ایک ID تیار کریں یا اسے خود سیٹ کریں۔

استعمال کرتے ہوئے خودکار طور پر تیار کردہ ID addDoc()

// create-task.js
import { collection, addDoc, Timestamp } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function createTask() {
  try {
    const docRef = await addDoc(collection(db, "tasks"), {
      title: "Write CRUD article",
      completed: false,
      tags: ["writing", "firebase"],
      metadata: {
        priority: "high",
        dueDate: Timestamp.fromDate(new Date("2026-09-15")),
      },
      createdAt: Timestamp.now(),
    });
    console.log("Task created with ID:", docRef.id);
  } catch (error) {
    console.error("Error creating task:", error);
  }
}

createTask();

حسب ضرورت ID بشمول: setDoc()

اگر آپ دستاویز IDs پر براہ راست کنٹرول حاصل کرنا چاہتے ہیں تو اس خصوصیت کا استعمال کریں، مثال کے طور پر دستاویز کی IDs کو دوسرے سسٹمز میں IDs سے ملانا۔

import { doc, setDoc } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function createTaskWithId(taskId) {
  await setDoc(doc(db, "tasks", taskId), {
    title: "Review pull request",
    completed: false,
    tags: ["code-review"],
  });
}

createTaskWithId("task-001");

ذیلی مجموعوں میں دستاویزات شامل کریں۔

اگر آپ کسی مخصوص کام میں تفصیل شامل کرنا چاہتے ہیں، تو براہ کرم پہلے بنیادی مضمون کا حوالہ دیں۔

import { collection, addDoc, Timestamp } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function addComment(taskId, text) {
  await addDoc(collection(db, "tasks", taskId, "comments"), {
    text,
    createdAt: Timestamp.now(),
  });
}

addComment("task-001", "First draft done");

مرحلہ 4 – پڑھیں: آپریشن کا سوال

واحد دستاویز درآمد کریں۔

import { doc, getDoc } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function getTask(taskId) {
  const snapshot = await getDoc(doc(db, "tasks", taskId));
  if (snapshot.exists()) {
    console.log(snapshot.id, snapshot.data());
  } else {
    console.log("No such task.");
  }
}

getTask("task-001");

پورا مجموعہ حاصل کریں۔

import { collection, getDocs } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function getAllTasks() {
  const snapshot = await getDocs(collection(db, "tasks"));
  snapshot.forEach((doc) => {
    console.log(doc.id, doc.data());
  });
}

getAllTasks();

استفسار کے لحاظ سے فلٹر کریں۔

import { collection, query, where, orderBy, limit, getDocs } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function getUrgentPendingTasks() {
  const q = query(
    collection(db, "tasks"),
    where("completed", "==", false),
    orderBy("metadata.priority"),
    limit(10)
  );

  const snapshot = await getDocs(q);
  snapshot.forEach((doc) => console.log(doc.id, doc.data()));
}

getUrgentPendingTasks();

احتیاط: مجموعہ where() ایک میدان میں orderBy() دوسری طرف، اکثر جامع انڈیکس. فائر اسٹور کنسول میں اسے پیدا کرنے کے لیے براہ راست لنک کے ساتھ ایک غلطی پھینک دیتا ہے۔ اس بارے میں مزید معلومات کے لیے، ڈیبگنگ دیکھیں۔

ریئل ٹائم اپڈیٹس onSnapshot()

آپ ریئل ٹائم تبدیلیوں کو صرف ایک بار لانے کے بجائے سبسکرائب کر سکتے ہیں۔ یہ کام کی فہرستوں کے لیے مفید ہے جو متعدد آلات پر فوری طور پر اپ ڈیٹ ہو جاتی ہیں۔

import { collection, onSnapshot } from "firebase/firestore";
import { db } from "./firebase-config.js";

const unsubscribe = onSnapshot(collection(db, "tasks"), (snapshot) => {
  snapshot.docChanges().forEach((change) => {
    console.log(change.type, change.doc.id, change.doc.data());
  });
});

// Call unsubscribe() when you no longer need updates (e.g., component unmount)

مرحلہ 5 – اپ ڈیٹ کریں: کام میں ترمیم کریں۔

جزوی اپ ڈیٹ updateDoc()

غیر مساوی setDoc()، updateDoc() یہ صرف آپ کے بیان کردہ فیلڈز کو چھوتا ہے۔ دستاویز میں باقی تمام مواد وہی رہتا ہے۔

import { doc, updateDoc } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function completeTask(taskId) {
  await updateDoc(doc(db, "tasks", taskId), {
    completed: true,
  });
}

completeTask("task-001");

ڈاٹ نوٹیشن کے ساتھ نیسٹڈ فیلڈز کو اپ ڈیٹ کرنا

پوری چیز کو دوبارہ لکھنے کی ضرورت نہیں ہے۔ metadata نقشے کے اندر کسی ایک پراپرٹی کو تبدیل کرنے کے لیے:

await updateDoc(doc(db, "tasks", "task-001"), {
  "metadata.priority": "low",
});

اپنی صف کو محفوظ طریقے سے اپ ڈیٹ کریں۔

ارے فیلڈز کو براہ راست اوور رائٹ کرنا ہم آہنگی کے حالات میں خطرناک ہے۔ استعمال کریں arrayUnion() اور arrayRemove() اس کے بجائے:

import { doc, updateDoc, arrayUnion, arrayRemove } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function addTag(taskId, tag) {
  await updateDoc(doc(db, "tasks", taskId), {
    tags: arrayUnion(tag),
  });
}

async function removeTag(taskId, tag) {
  await updateDoc(doc(db, "tasks", taskId), {
    tags: arrayRemove(tag),
  });
}

arrayUnion() ڈپلیکیٹ اقدار شامل نہ کریں۔ arrayRemove() تمام مماثل مثالوں کو ہٹاتا ہے۔ دونوں جوہری طور پر سرور پر کام کرتے ہیں۔

مرحلہ 6 – حذف کریں: کام کو ہٹا دیں۔

دستاویز کو حذف کریں۔

import { doc, deleteDoc } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function deleteTask(taskId) {
  await deleteDoc(doc(db, "tasks", taskId));
}

deleteTask("task-001");

سب کلیکشن ٹریپس

پہلے ذکر کردہ مسائل یہ ہیں: حذف کریں۔ tasks/task-001 کرو ~ نہیں حذف کریں comments ذیلی مجموعہ۔ تشریح شدہ دستاویز علیحدہ ہے اور اب بھی ڈیٹا بیس میں موجود ہے۔ اگر آپ راستہ نہیں جانتے ہیں، تو آپ UI کے ذریعے اس تک رسائی حاصل نہیں کر سکیں گے۔

صحیح طریقے سے صاف کرنے کے لیے، پہلے چائلڈ کلیکشن میں موجود دستاویزات کو ڈیلیٹ کریں، پھر پیرنٹ کلیکشن کو ڈیلیٹ کریں۔

import { collection, getDocs, doc, deleteDoc, writeBatch } from "firebase/firestore";
import { db } from "./firebase-config.js";

async function deleteTaskWithComments(taskId) {
  const commentsRef = collection(db, "tasks", taskId, "comments");
  const commentsSnapshot = await getDocs(commentsRef);

  const batch = writeBatch(db);
  commentsSnapshot.forEach((commentDoc) => {
    batch.delete(commentDoc.ref);
  });
  batch.delete(doc(db, "tasks", taskId));

  await batch.commit();
}

deleteTaskWithComments("task-001");

writeBatch() متعدد حذفوں کو ایک ایٹم آپریشن میں گروپ کرتا ہے۔ یا تو وہ سب کامیاب ہوتے ہیں یا ان میں سے کوئی بھی کامیاب نہیں ہوتا۔

ایک فیلڈ کو حذف کریں۔

پوری دستاویز کو حذف کیے بغیر صرف ایک فیلڈ کو ہٹانے کے لیے، استعمال کریں: deleteField():

import { doc, updateDoc, deleteField } from "firebase/firestore";
import { db } from "./firebase-config.js";

await updateDoc(doc(db, "tasks", "task-001"), {
  metadata: deleteField(),
});

عام مسائل کو ڈیبگ کرنا

FirebaseError: Missing or insufficient permissions

حفاظتی اصول درخواست کو روک رہا ہے۔ اگر آپ ابھی بھی ٹیسٹنگ موڈ میں ہیں، تو یقینی بنائیں کہ 30 دن کی مدت ختم ہو گئی ہے (جس کے بعد یہ اصول Deny All پر واپس آ جائے گا)۔ حقیقی ایپس کے لیے، یہاں قواعد کا جائزہ لیں: فائر اسٹیشن پھر حکمرانی اس بات کو یقینی بنائیں کہ آپ کے رول بلاکس پڑھنے اور لکھنے کے راستوں سے مماثل ہیں، بشمول کوئی بھی ذیلی مجموعہ جس کے لیے ان کی ضرورت ہے۔

Function addDoc() called with invalid data. Unsupported field value: undefined

فائر اسٹور مسترد کرتا ہے۔ undefined اس کے برعکس، قدریں اچھی طرح سے ہیں۔ nullیہ قابل قبول ہے۔ یہ عام طور پر اس وقت ہوتا ہے جب فارم کا فیلڈ خالی ہوتا ہے اور آپ اسے سیدھے رائٹ کال پر بھیج دیتے ہیں۔ فلٹرنگ undefined فیلڈ لکھنے سے پہلے، یا بطور ڈیفالٹ درج کریں۔ null.

The query requires an index

جب ملایا جائے تو ایسا لگتا ہے۔ where() اور orderBy() مختلف فیلڈز پر لاگو ہوتا ہے، جیسا کہ مرحلہ 4 کی مثال میں دکھایا گیا ہے۔ Firestore اس استفسار کو خودکار اشاریہ سازی کے ساتھ پیش نہیں کر سکتا۔ غلطی کے پیغام میں کنسول میں کمپوزٹ انڈیکس کو پہلے سے آباد کرنے کے لیے براہ راست لنک شامل ہے، اس پر کلک کریں، انڈیکس بننے کے لیے ایک یا دو منٹ انتظار کریں، اور پھر استفسار کو دوبارہ چلائیں۔

تیزی سے مزید پڑھیں/کوٹہ وارننگ

تمام دستاویزات کی طرف سے واپس getDocs() یہ لوپ کال کے اندر پڑھے جانے کے طور پر بھی شمار ہوتا ہے۔ getDoc() بار بار کلائنٹ سائیڈ پر فلٹر کرنے کے لیے پورے مجموعہ کو کھینچنے سے گریز کریں اور فلٹرنگ کو استفسار پر دھکیلیں: where() اس کے بجائے limit() ہر اس چیز کے بارے میں جو لامحدود بڑھ سکتی ہے۔

حذف ہونے کے بعد یتیم ذیلی جمع

اگر کوئی دستاویز جس کے بارے میں آپ نے سوچا تھا کہ آپ نے حذف کر دیا ہے وہ اب بھی آپ کا ذخیرہ استعمال کر رہی ہے یا آپ کی برآمد میں دکھائی دے رہی ہے، حذف شدہ دستاویز کے راستے کے نیچے ذیلی جمع چیک کریں۔ جیسا کہ مرحلہ 6 میں دکھایا گیا ہے: deleteDoc() یہ کبھی نہیں جھڑتا ہے اور صفائی ہمیشہ آپ کی ذمہ داری ہے۔

یہ ایک سرکلر فلو چارٹ ہے جو لگاتار چکروں میں چار CRUD آپریشن دکھاتا ہے: تخلیق، پڑھیں، اپ ڈیٹ، اور حذف کریں۔ ہر آپریشن پر متعلقہ Firestore JavaScript فنکشن (addDoc/setDoc, getDoc/getDocs/onSnapshot, updateDoc/arrayUnion، deleteDoc/writeBatch) کے ساتھ لیبل لگا ہوا ہے اور یہ دکھاتا ہے کہ یہ آپریشنز ایک عام ڈیٹا لائف سائیکل سے کیسے متعلق ہیں۔

نتیجہ

اب آپ کے پاس Firestore ڈھانچہ کا ورکنگ مینٹل ماڈل ہے اور ویب SDK v9+ کا استعمال کرتے ہوئے تمام CRUD آپریشنز کا تجربہ ہے۔ یہاں ایک مختصر خلاصہ ہے:

کام اہم خصوصیات
بنانا addDoc()، setDoc()
پڑھیں getDoc()، getDocs()، query()، onSnapshot()
اپ ڈیٹ updateDoc()، arrayUnion()، arrayRemove()
حذف کریں deleteDoc()، deleteField()، writeBatch()

ایک بار جب آپ بنیادی باتوں سے راضی ہوجائیں تو، یہاں کچھ قدرتی اگلے اقدامات ہیں۔

  • کامپڑھنے اور لکھنے کے لیے جو ایک ساتھ کامیاب یا ناکام ہونا چاہیے (مثلاً دو صارفین کے درمیان آپریشن بھیجنا)

  • بیچ لکھناآپ اسے پہلے ہی مرحلہ 6 میں دیکھ چکے ہیں۔ جب بھی آپ کو متعدد دستاویزات کو جوہری طور پر پروسیس کرنے کی ضرورت ہو تو یہ مفید ہے۔

  • جامع انڈیکساعلی درجے کی فلٹرنگ اور چھانٹنے والے امتزاج

  • صفحہ بندی کے ساتھ startAfter()بڑے مجموعوں کو ایک ساتھ لوڈ کرنے کے بجائے ٹکڑوں میں لوڈ کرنا

اگر آپ نے پہلے سے ایسا نہیں کیا ہے تو، آپ ڈیٹا ماڈلنگ کے طریقوں کو دوبارہ دیکھنا چاہتے ہیں۔ پہلے آپ اس کے لیے استفسار لکھیں۔ ماڈلنگ کے مرحلے کے دوران کیے گئے فیصلے (جیسے کہ ڈیٹا کو گھوںسلا کرنا ہے یا ذیلی مجموعہ کا استعمال کرنا ہے) براہ راست اس بات کا تعین کرتے ہیں کہ ان میں سے کون سا CRUD پیٹرن بعد میں قدرتی محسوس ہوتا ہے اور کون سا عجیب لگتا ہے۔

اوپر تک سکرول کریں۔