Node.js va SQLite: baza bilan ishlash

CLI dastur darsida vazifalarni todos.json fayliga saqlagansiz. Bu kichik ro’yxatda ishlaydi. Ammo ma’lumot o’n mingtaga yetganda muammo boshlanadi: bitta vazifani o’zgartirish uchun ham butun fayl o’qiladi, JSON.parse qilinadi va qaytadan yoziladi.

Baza aynan shu muammoni hal qiladi: kerakli qatorni butun faylni o’qimasdan topadi va yangilaydi.

node:sqlite — o’rnatish shart emas

Node.js’da SQLite ichiga qurilgan. npm orqali hech narsa o’rnatilmaydi:

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync(":memory:");

database.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
console.log("Jadval yaratildi");

database.close();
node sqlite-check.js
Jadval yaratildi
(node:10640) ExperimentalWarning: SQLite is an experimental feature and might change at any time

":memory:" — baza faylsiz, faqat xotirada yaratiladi. Dastur yopilgach yo’qoladi, shuning uchun sinash uchun qulay.

Fayldagi baza va jadval

Endi haqiqiy fayl bilan ishlaymiz. db-demo.js:

const { DatabaseSync } = require("node:sqlite");

// Fayl bo'lmasa o'zi yaratiladi
const database = new DatabaseSync("books.db");

database.exec(`
  CREATE TABLE IF NOT EXISTS books (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    author TEXT NOT NULL,
    year INTEGER
  )
`);

console.log("Jadval tayyor");

database.close();
node db-demo.js
Jadval tayyor

Papkada books.db fayli paydo bo’ladi — butun baza shu faylda.

IF NOT EXISTS qo’shilgani muhim: dastur ikkinchi marta ishga tushganda jadval bor bo’lgani uchun xato chiqmaydi.

Jadval tuzilishi CREATE TABLE darsidagi yozuvning o’zi. SQL bilimi shu yerda to’g’ridan-to’g’ri ishlaydi.

exec va prepare farqi

Modulda ikki xil usul bor va ularni chalkashtirmaslik kerak:

Metod Qachon Qiymat qabul qiladimi
database.exec() Jadval yaratish, o’chirish Yo’q
database.prepare() Ma’lumot qo’shish, o’qish, yangilash Ha

Ma’lumot qo’shish — run

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync("books.db");

const insert = database.prepare(
  "INSERT INTO books (title, author, year) VALUES (?, ?, ?)"
);

const added = insert.run("O'tkan kunlar", "Abdulla Qodiriy", 1926);
console.log("Qo'shildi, id:", added.lastInsertRowid);

insert.run("Mehrobdan chayon", "Abdulla Qodiriy", 1929);
insert.run("Shum bola", "G'afur G'ulom", 1936);

database.close();
node insert-demo.js
Qo'shildi, id: 1

So’rovdagi ? belgilari — qiymat o’rni. run() ga berilgan argumentlar o’sha o’rinlarga tartib bo’yicha tushadi.

run() natijasida ikkita foydali maydon qaytadi:

Maydon Nima
lastInsertRowid Yangi qatorning id si
changes O’zgargan qatorlar soni

Bir marta tayyorlangan insert uch marta ishlatilgani ham muhim — har safar qaytadan tayyorlash shart emas.

Ma’lumot o’qish — all va get

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync("books.db");

// Hamma qatorlar — massiv qaytadi
const all = database.prepare("SELECT * FROM books ORDER BY year").all();

console.log("Jami kitoblar:", all.length);
for (const book of all) {
  console.log(`${book.year} — ${book.title} (${book.author})`);
}

// Bitta qator — obyekt qaytadi
const one = database
  .prepare("SELECT * FROM books WHERE author = ?")
  .get("G'afur G'ulom");

console.log("Topildi:", one.title);

database.close();
node select-demo.js
Jami kitoblar: 3
1926 — O'tkan kunlar (Abdulla Qodiriy)
1929 — Mehrobdan chayon (Abdulla Qodiriy)
1936 — Shum bola (G'afur G'ulom)
Topildi: Shum bola

SELECT va ORDER BY yozuvi select darsidagi bilan bir xil — o’zgargani faqat chaqirish usuli.

Metod Qaytaradi Hech narsa topilmasa
.all() Qatorlar massivi Bo’sh massiv []
.get() Bitta qator obyekti undefined
.run() O’zgarish haqida ma’lumot

Yangilash va o’chirish

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync("books.db");

const updated = database
  .prepare("UPDATE books SET year = ? WHERE title = ?")
  .run(1925, "O'tkan kunlar");

console.log("O'zgargan qatorlar:", updated.changes);

const deleted = database
  .prepare("DELETE FROM books WHERE year < ?")
  .run(1900);

console.log("O'chirilgan qatorlar:", deleted.changes);

database.close();
node update-demo.js
O'zgargan qatorlar: 1
O'chirilgan qatorlar: 0

changes qiymati 0 bo’lsa — shartga mos qator topilmagan. Bu xato emas, lekin dastur buni sezishi kerak: “o’zgartirildi” deb xabar bermasdan oldin changes ni tekshiring.

Eng muhim qism: parametrli so’rov

Endi ? belgisi nega kerakligini ko’rsatamiz. Bu darsning eng muhim bo’limi.

Tasavvur qiling, qidiruv so’zi foydalanuvchidan keladi. Uni so’rovga yopishtirib yozish mumkin edi. Nima bo’lishini sinaymiz:

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync(":memory:");
database.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
database.prepare("INSERT INTO users (name) VALUES (?)").run("Aziz");
database.prepare("INSERT INTO users (name) VALUES (?)").run("Malika");

// Foydalanuvchi oddiy ism o'rniga SQL bo'lagini yozdi
const userInput = "Aziz' OR '1'='1";

const rows = database
  .prepare("SELECT * FROM users WHERE name = ?")
  .all(userInput);

console.log("Parametr bilan topilgan qatorlar:", rows.length);

const normal = database
  .prepare("SELECT * FROM users WHERE name = ?")
  .all("Aziz");

console.log("Oddiy qidiruv:", normal.length, normal[0].name);

database.close();
node injection-demo.js
Parametr bilan topilgan qatorlar: 0
Oddiy qidiruv: 1 Aziz

Natijaga diqqat qiling. Hujum uchun yozilgan matn 0 ta qator qaytardi.

Sabab: ? orqali uzatilgan qiymat SQL kodi sifatida o’qilmaydi. Baza uni butunligicha oddiy ism deb qabul qildi va shunday ismli foydalanuvchi topilmadi. Ya’ni ' va OR belgilari o’z kuchini yo’qotdi.

Nomlangan parametrlar

Parametr ko’payganda ? belgilari chalkashadi. Ularga nom berish mumkin:

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync("books.db");

const found = database
  .prepare(
    "SELECT * FROM books WHERE author = :author AND year >= :minYear"
  )
  .all({ author: "Abdulla Qodiriy", minYear: 1925 });

console.log("Topilgan kitoblar:", found.length);
for (const book of found) {
  console.log(`${book.title} — ${book.year}`);
}

database.close();
node named-demo.js
Topilgan kitoblar: 2
O'tkan kunlar — 1925
Mehrobdan chayon — 1929

Himoya darajasi bir xil, o’qish esa qulayroq.

Jadvallarni birlashtirish

SQL ning kuchi bir nechta jadval bilan ochiladi:

const { DatabaseSync } = require("node:sqlite");

const database = new DatabaseSync(":memory:");

database.exec(`
  CREATE TABLE authors (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
  );
  CREATE TABLE novels (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    authorId INTEGER,
    FOREIGN KEY (authorId) REFERENCES authors(id)
  );
`);

database.prepare("INSERT INTO authors (id, name) VALUES (?, ?)").run(1, "Abdulla Qodiriy");
database.prepare("INSERT INTO novels (title, authorId) VALUES (?, ?)").run("O'tkan kunlar", 1);
database.prepare("INSERT INTO novels (title, authorId) VALUES (?, ?)").run("Mehrobdan chayon", 1);

const rows = database
  .prepare(
    `SELECT novels.title, authors.name AS authorName
     FROM novels
     JOIN authors ON novels.authorId = authors.id`
  )
  .all();

for (const row of rows) {
  console.log(`${row.title} — ${row.authorName}`);
}

database.close();
node join-demo.js
O'tkan kunlar — Abdulla Qodiriy
Mehrobdan chayon — Abdulla Qodiriy

JOIN yozuvi join darsidagi bilan aynan bir xil.

Amaliy mashq

node-start papkasida bajaring:

  1. books.db faylini yaratib, books jadvalini IF NOT EXISTS bilan qo’shing hamda skriptni ikki marta ishga tushiring;
  2. prepare va run bilan uchta kitob qo’shib, lastInsertRowid qiymatlarini chiqaring;
  3. .all() bilan hamma kitobni, .get() bilan bittasini o’qing va qaytgan turlarni solishtiring;
  4. Mavjud bo’lmagan id bilan .get() chaqirib, undefined holatini tekshiruv bilan qamrang;
  5. UPDATE so’rovini bajarib, changes qiymatiga qarab xabar chiqaring;
  6. "Aziz' OR '1'='1" matnini ? orqali qidiruvga berib, natija bo’sh ekanini o’z ko’zingiz bilan ko’ring.

6-qadam eng muhimi: parametrli so’rov nega ishlashini bir marta ko’rgan odam uni boshqa unutmaydi.

Qisqacha xulosa

  • node:sqlite Node.js ichida keladi — npm install qilinmaydi.
  • Modul eksperimental: ishlaydi, lekin ogohlantirish chiqaradi.
  • Butun baza bitta faylda saqlanadi, ":memory:" esa faqat xotirada.
  • exec() qiymatsiz SQL uchun, prepare() qiymat bilan ishlaydigan so’rovlar uchun.
  • .all() massiv, .get() bitta obyekt yoki undefined, .run() o’zgarish ma’lumotini qaytaradi.
  • Qiymat har doim ? yoki nomlangan parametr orqali beriladi — so’rov matniga yopishtirilmaydi.
  • Parametrli so’rovda hujum uchun yozilgan matn oddiy qiymat bo’lib qoladi va hech nima topmaydi.

Keyingi darsda xatolar va jurnal — nosozlikni qanday qayd etish va topish.

Bu dars foydali bo'ldimi?

marta ko'rildi