এতদিন 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:
- একটা D1 database তৈরি করো, উপরের
schema.sqlদিয়েbooksটেবিল বানাও। - একটা Worker থেকে
INSERTদিয়ে ৩-৪টা বই যোগ করো, তারপরSELECT *দিয়ে সব দেখাও। - একটা
GROUP BYquery লিখে প্রতিটা লেখকের বইয়ের সংখ্যা বের করো। UPDATEআরDELETEদিয়ে ডেটা বদলানো আর মুছে ফেলা অনুশীলন করো।
পরের পর্বে যা থাকছে
এতদিন আমরা Pages, Workers, R2, KV, D1 — প্রতিটা সার্ভিস আলাদাভাবে শিখেছি। পরের পর্বে আমরা শিখব Environment Variables ও Secrets ম্যানেজমেন্ট — dev/staging/production-এর জন্য আলাদা কনফিগারেশন কীভাবে সংগঠিত রাখতে হয়, যা এই সব সার্ভিসকে একসাথে ব্যবহার করার সময় গুরুত্বপূর্ণ হয়ে ওঠে।