Подключение базы данных
Массив в памяти очищается при каждом перезапуске сервера. Чтобы статьи и комментарии сохранялись, нужна база данных. В курсе используется SQLite — вся база хранится в одном файле и не требует отдельного сервера, — а работает с ней пакет better-sqlite3. Запросы к базе пишутся на языке SQL.
Установка
npm install better-sqlite3Пакет содержит скомпилированную часть, поэтому установка может занять минуту.
Плагин базы данных
Подключение к базе нужно всем маршрутам, поэтому его оформляют плагином с fastify-plugin. Создайте src/plugins/db.js:
// src/plugins/db.js
import fp from 'fastify-plugin';
import Database from 'better-sqlite3';
async function dbPlugin(app) {
const db = new Database(app.config.DB_FILE);
db.pragma('foreign_keys = ON');
db.exec(`
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
body TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
author TEXT NOT NULL,
text TEXT NOT NULL
);
`);
app.decorate('db', db);
app.addHook('onClose', async () => db.close());
}
export default fp(dbPlugin);Плагин открывает файл из настройки DB_FILE, создаёт таблицы, если их ещё нет, добавляет app.db и закрывает базу при остановке сервера. ON DELETE CASCADE удалит комментарии вместе со статьёй.
Подключение
В src/app.js замените плагин store на db. Он должен идти после @fastify/env, потому что читает app.config:
// src/app.js
import dbPlugin from './plugins/db.js';
app.register(fastifyEnv, { schema: envSchema });
app.register(dbPlugin);
app.register(postRoutes);
app.register(commentRoutes);После npm run dev в корне проекта появится файл blog.db. Добавьте его в .gitignore.
Первый запрос
Методы better-sqlite3 синхронные: prepare() готовит запрос, а all(), get() и run() выполняют его.
// src/routes/posts.js
app.get('/posts', async () => {
return app.db.prepare('SELECT id, title FROM posts ORDER BY id DESC').all();
});Откройте http://localhost:3000/posts — пока база пуста, вы увидите []. Статьи начнут появляться в следующем уроке, когда маршрут POST будет записывать их в базу.
| Метод | Возвращает |
|---|---|
.all(...значения) | Массив строк |
.get(...значения) | Одну строку или undefined |
.run(...значения) | changes — сколько строк изменено, lastInsertRowid — id новой строки |
Частая ошибка
Нельзя вставлять данные от клиента прямо в текст запроса — это открывает дорогу SQL-инъекциям, когда злоумышленник подставляет свой SQL:
// src/routes/posts.js
// Неправильно:
app.db.prepare(`SELECT * FROM posts WHERE id = ${request.params.id}`).get();Ставьте в запрос знак ?, а значения передавайте отдельно: prepare('SELECT * FROM posts WHERE id = ?').get(id).