Cloudflare এর হাতেখড়ি (পর্ব ৯) – D1 পরিচিতি: SQLite ডেটাবেজ

Cloudflare D1 পরিচিতি SQLite ডেটাবেজ

এতদিন RTDB (JSON ট্রি), R2 (ফাইল), আর KV (কী-ভ্যালু ক্যাশ) শিখেছি। এবার একদম নতুন ধরনের একটা স্টোরেজ — D1, Cloudflare-এর SQLite-ভিত্তিক ডেটাবেজ। যদি Firebase এর হাতেখড়ি পর্ব ৪-এ Firestore-এর "Collection ও Document" ধারণা মনে থাকে, D1 তার চেয়েও আরেক ধাপ এগিয়ে — এখানে ডেটা থাকে চেনা SQL টেবিল-এ, ঠিক যেমন স্কুলে/ইউনিভার্সিটিতে SQL শেখানো হয়।

D1 কী?

D1 হলো একটা সম্পূর্ণ SQLite ডেটাবেজ, যা Cloudflare-এর নেটওয়ার্কে চলে। SQLite মানে — টেবিল, কলাম, রো, আর SQL ভাষায় query — SELECT, INSERT, UPDATE, DELETE

RTDB vs D1 — গঠনগত পার্থক্য

// RTDB — JSON ট্রি (Firebase এর হাতেখড়ি পর্ব ৫)
{
  "books": {
    "book1": { "title": "অপরাজিত", "author": "বিভূতিভূষণ", "year": 1932 }
  }
}

// D1 — SQL টেবিল
+----+---------------------+------------------+------+
| id | title               | author           | year |
+----+---------------------+------------------+------+
| 1  | অপরাজিত             | বিভূতিভূষণ        | 1932 |
+----+---------------------+------------------+------+

কখন D1, কখন RTDB

বিষয় RTDB D1
রিয়েল-টাইম সিঙ্ক বিল্ট-ইন (onValue) নেই, নিজে বানাতে হয়
জটিল সম্পর্কিত ডেটা (relations) ম্যানুয়ালি ম্যানেজ করা কঠিন JOIN দিয়ে স্বাভাবিক
রিপোর্টিং, অ্যাগ্রিগেশন কঠিন (JS দিয়ে ম্যানুয়াল) SUM, COUNT, GROUP BY সহজ
সেরা ব্যবহার চ্যাট, লাইভ প্রেজেন্স, ফ্রন্টএন্ড থেকে সরাসরি অর্ডার হিস্ট্রি, ইনভেন্টরি, রিপোর্ট, বিশ্লেষণ

গুরুত্বপূর্ণ পার্থক্য: RTDB-এর মতো D1 ফ্রন্টএন্ড থেকে সরাসরি সংযোগ দেয় না — D1 সবসময় Worker-এর মাধ্যমে ব্যবহার হয় (RTDB Security Rules-এর তুলনায় D1-এ অ্যাক্সেস কন্ট্রোল আপনাকেই Worker কোডে লিখতে হয়)।

Database তৈরি করা

wrangler d1 create book-inventory

# আউটপুটে একটা database_id পাওয়া যাবে
# wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "book-inventory"
database_id = "abc123def456..."

টেবিল বানানো

একটা schema.sql ফাইলে টেবিলের গঠন লেখা হয়।

-- schema.sql
CREATE TABLE books (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  author TEXT NOT NULL,
  year INTEGER,
  owner_id TEXT
);
wrangler d1 execute book-inventory --file=./schema.sql

Worker থেকে Query চালানো

Worker-এর মধ্যে env.DB দিয়ে SQL চালানো যায় — prepare() দিয়ে query বানিয়ে .bind() দিয়ে ভ্যালু বসানো (SQL Injection ঠেকাতে, সরাসরি স্ট্রিং জোড়া দেওয়া না)।

export default {
  async fetch(request, env, ctx) {
    // INSERT
    await env.DB.prepare(
      "INSERT INTO books (title, author, year, owner_id) VALUES (?, ?, ?, ?)"
    ).bind("অপরাজিত", "বিভূতিভূষণ", 1932, "user123").run();

    // SELECT — সব বই
    const { results } = await env.DB.prepare("SELECT * FROM books").all();

    return new Response(JSON.stringify(results), {
      headers: { "content-type": "application/json" },
    });
  },
};

লক্ষ্য করুন — ? চিহ্নগুলো placeholder, আর .bind()-এ দেওয়া মান যথাক্রমে সেগুলোতে বসে। এটা RTDB-এর মতো টেমপ্লেট স্ট্রিং দিয়ে সরাসরি ইউজার ইনপুট জোড়া দেওয়া (`SELECT * FROM books WHERE title = '${input}'`) থেকে অনেক বেশি নিরাপদ।

ফিল্টার ও শর্ত — WHERE

// শুধু একটা নির্দিষ্ট ইউজারের বই
const { results } = await env.DB.prepare(
  "SELECT * FROM books WHERE owner_id = ?"
).bind("user123").all();

Firebase এর হাতেখড়ি পর্ব ৭-এর orderByChild + equalTo-এর তুলনায় এখানে WHERE ক্লজ অনেক বেশি নমনীয় — একাধিক শর্ত AND/OR দিয়ে একসাথে জোড়া যায়।

Aggregation — যা RTDB-তে কঠিন

এইখানেই D1-এর আসল শক্তি — একটা লেখকের কতগুলো বই আছে তা গুনতে RTDB-তে সব ডেটা এনে JS দিয়ে গুনতে হতো, D1-এ এক লাইনের SQL।

// প্রতিটা লেখকের কতগুলো বই আছে
const { results } = await env.DB.prepare(`
  SELECT author, COUNT(*) as total
  FROM books
  GROUP BY author
  ORDER BY total DESC
`).all();

// ফলাফল: [{ author: "বিভূতিভূষণ", total: 3 }, { author: "রবীন্দ্রনাথ", total: 5 }, ...]

UPDATE ও DELETE

// UPDATE
await env.DB.prepare("UPDATE books SET year = ? WHERE id = ?")
  .bind(1933, 1).run();

// DELETE
await env.DB.prepare("DELETE FROM books WHERE id = ?")
  .bind(1).run();

ইন্টারেক্টিভ — SQL Query বিল্ডার

নিচে বিভিন্ন SQL কমান্ড বেছে নিয়ে দেখো query কেমন দেখতে হয়, আর কোন কাজে কোনটা ব্যবহার হয়।

ভালো অভ্যাস

  • সবসময় .bind() ব্যবহার করুন — ইউজার ইনপুট সরাসরি SQL স্ট্রিং-এ জোড়া দিলে SQL Injection-এর ঝুঁকি থাকে
  • জটিল রিপোর্টিং/অ্যাগ্রিগেশন দরকার হলে D1 বেছে নিন, শুধু সাধারণ CRUD-এ RTDB-ই যথেষ্ট
  • D1-এ Auth নিজে চেক করতে হবে — RTDB Security Rules-এর মতো বিল্ট-ইন সুরক্ষা নেই, তাই Worker কোডেই owner_id যাচাই করতে হবে

নিজে চেষ্টা করো

Exercise:

  1. একটা D1 database তৈরি করো, উপরের schema.sql দিয়ে books টেবিল বানাও।
  2. একটা Worker থেকে INSERT দিয়ে ৩-৪টা বই যোগ করো, তারপর SELECT * দিয়ে সব দেখাও।
  3. একটা GROUP BY query লিখে প্রতিটা লেখকের বইয়ের সংখ্যা বের করো।
  4. UPDATE আর DELETE দিয়ে ডেটা বদলানো আর মুছে ফেলা অনুশীলন করো।

পরের পর্বে যা থাকছে

এতদিন আমরা Pages, Workers, R2, KV, D1 — প্রতিটা সার্ভিস আলাদাভাবে শিখেছি। পরের পর্বে আমরা শিখব Environment Variables ও Secrets ম্যানেজমেন্ট — dev/staging/production-এর জন্য আলাদা কনফিগারেশন কীভাবে সংগঠিত রাখতে হয়, যা এই সব সার্ভিসকে একসাথে ব্যবহার করার সময় গুরুত্বপূর্ণ হয়ে ওঠে।

একটি মন্তব্য পোস্ট করুন

নবীনতর পূর্বতন

যোগাযোগ ফর্ম