Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

База данных SQLite

JSON-файл перестаёт справляться, когда статей становятся тысячи или два запроса пытаются записать файл одновременно. Тогда данные переносят в базу данных. SQLite — небольшая база, которая хранится в одном файле и не требует отдельного сервера. В Node.js 22.13 и новее она встроена: модуль node:sqlite. Язык запросов SQL здесь используется в самом простом виде.

Подключение и таблица

Класс DatabaseSync открывает файл базы (или создаёт его). Метод exec() выполняет SQL-команду:

import { DatabaseSync } from 'node:sqlite';
 
const db = new DatabaseSync('blog.db');
 
db.exec(`
  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    body TEXT NOT NULL DEFAULT ''
  )
`);
 
console.log('Таблица posts готова');

Рядом появится файл blog.db. CREATE TABLE IF NOT EXISTS создаёт таблицу только при первом запуске, а AUTOINCREMENT сам выдаёт номера статей. Вместо имени файла можно передать ':memory:' — база будет жить только в памяти.

Вставка

Запрос с данными готовят методом prepare(), а значения подставляют на место знаков ?:

import { DatabaseSync } from 'node:sqlite';
 
const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, body TEXT)');
 
const insert = db.prepare('INSERT INTO posts (title, body) VALUES (?, ?)');
const result = insert.run('Привет, SQLite', 'Первая статья в базе');
 
console.log(result.lastInsertRowid); // 1

run() возвращает объект с номером новой строки и числом изменённых строк (changes).

Выборка

all() возвращает массив всех найденных строк, get() — одну строку или undefined:

import { DatabaseSync } from 'node:sqlite';
 
const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT)');
const insert = db.prepare('INSERT INTO posts (title) VALUES (?)');
insert.run('Привет, Node.js');
insert.run('Работа с файлами');
 
const posts = db.prepare('SELECT id, title FROM posts ORDER BY id').all();
for (const post of posts) console.log(post.id, post.title);
 
const post = db.prepare('SELECT title FROM posts WHERE id = ?').get(2);
console.log(post.title); // Работа с файлами

Выведутся две строки со статьями, затем заголовок второй.

В проекте блога

Модуль src/posts.js можно переписать на базу, сохранив те же функции — сервер ничего не заметит:

// src/posts.js
import { DatabaseSync } from 'node:sqlite';
 
const db = new DatabaseSync('blog.db');
 
export async function loadPosts() {
  return db.prepare('SELECT * FROM posts ORDER BY id DESC').all();
}
 
export async function findPost(id) {
  return db.prepare('SELECT * FROM posts WHERE id = ?').get(id);
}

Методы node:sqlite синхронные, а async оставлен, чтобы код сервера с await работал без изменений. Если ваша версия Node.js старше 22.13, используйте пакет better-sqlite3 — у него очень похожий интерфейс.

Частая ошибка

Нельзя вставлять данные от пользователя прямо в текст запроса:

// src/posts.js
// Опасно: db.prepare(`SELECT * FROM posts WHERE title = '${title}'`)

Заголовок вида ' OR '1'='1 изменит смысл запроса — это SQL-инъекция. Всегда передавайте значения через ? и аргументы run(), get() или all(): база подставит их безопасно.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.