Создание приложения на JavaScript для работы с базой данных РЕД База Данных
В этом руководстве мы создадим простое веб‑приложение на JavaScript, которое будет взаимодействовать с базой данных РЕД База Данных. Приложение будет использовать Express.js для создания сервера и библиотеку node-firebird-driver-native для работы с РЕД База Данных. Мы также добавим простой графический интерфейс для управления контактами в базе данных.
Установка необходимых инструментов
- Установите Node.js и npm (также здесь устанавливаются g++ (gcc-c++), firebird-utils, firebird-devel, так как они понадобятся для работы библиотеки node-firebird-driver-native):
sudo dnf install nodejs npm g++ firebird-utils firebird-devel
Для проверки того, что у вас всё установилось, выполните команды:
node -v
npm -v

- Установка Visual Studio Code (VS Code)
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'
sudo dnf check-update
sudo dnf install code
Написание программы
- Создадим папку, в которой будут находиться исходные файлы проекта.
mkdir ~/js_tutorial
- Откроем эту папку через VS Code.
code ~/js_tutorial
- Используя сочетание клавиш Ctrl+J, откройте терминал и выполните команду инициализации:
npm init -y
В корневой директории проекта появится новый файл package.json

- Установим необходимые зависимости:
npm install express ejs node-firebird-driver-native

- Подготовим структуру проекта:
/js_tutorial
├── /.vscode ## ???? Настройки vs code
│ ├── launch.json ## ???? Конфигурация запуска и отладки
├── /db ## ???? База данных РЕД База Данных (файл .fdb)
│ ├── phonebook.fdb ## ???? Файл базы данных
├── /public ## ???? Статические файлы (CSS, JS, изображения)
│ ├── styles.css
├── /views ## ???? Шаблоны EJS (интерфейс пользователя)
│ ├── index.ejs
├── /config
│ ├── dbConfig.js ## ???? Конфигурация подключения к БД
├── /models
│ ├── db.js ## ???? Подключение к РЕД База Данных
│ ├── contactModel.js ## ???? Логика работы с контактами
├── /routes
│ ├── contactRoutes.js ## ???? Маршруты для API
├── server.js ## ???? Запуск сервера
Для удобного запуска сервера в launch.json пропишите:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Запуск сервера",
"program": "${workspaceFolder}/server.js",
"console": "integratedTerminal",
"restart": true,
"env": {
"NODE_ENV": "development"
}
},
{
"type": "node",
"request": "attach",
"name": "Подключиться к запущенному процессу",
"processId": "${command:PickProcess}"
}
]
}
- Конфигурация базы данных (config/dbConfig.js)
const path = require('path');
module.exports = {
host: 'localhost',
port: 3050,
user: 'sysdba',
password: 'masterkey',
database: path.resolve(__dirname, '../db/phonebook.fdb')
};
В этом файле настраиваем параметры подключения к БД.
Путь к файлу базы данных должен быть строкой; в противном случае РЕД База Данных вернёт ошибку.
- Подключение к базе данных (models/db.js). Этот файл отвечает за:
- Подключение к базе данных
- Создание базы данных
- Создание таблицы
contacts, если она не существует - Экспорт функций для работы с БД
- Импорт зависимостей
const fs = require('fs');
const { createNativeClient, getDefaultLibraryFilename } = require('node-firebird-driver-native');
const dbConfig = require('../config/dbConfig');
const { addContact } = require('./contactModel');
fs— проверяет существование файла базы (fs.existsSync()).createNativeClient()— создаёт клиент РЕД База Данных.getDefaultLibraryFilename()— автоматически определяет путь к библиотеке РЕД Базы Данных.dbConfig— параметры подключения к базе данных.addContact— функция для добавления контактов.
- Создание клиента РЕД Базы Данных
const client = createNativeClient(getDefaultLibraryFilename());
- Создаём клиента РЕД Базы Данных (
client). getDefaultLibraryFilename()автоматически выбирает нужную библиотеку.
- Функция подключения к базе (connectToDatabase)
async function connectToDatabase() {
try {
console.log('???? Подключение к БД:', dbConfig.database);
console.log('???? Тип переменной database:', typeof dbConfig.database);
return await client.connect(dbConfig.database, {
user: dbConfig.user,
password: dbConfig.password
});
} catch (error) {
console.error('Ошибка подключения к базе данных:', error);
throw error;
}
}
- Логируем подключение (console.log) — полезно для отладки.
- client.connect():
database— путь к файлу базы данных (.fdb);user/password— учётные данные РЕД Базы Данных. - Если подключение не удаётся — выводим ошибку (catch (error)).
- Создание базы данных (createDatabase)
async function createDatabase() {
if (fs.existsSync(dbConfig.database)) {
console.log('База данных уже существует.');
return;
}
try {
console.log(`Создаём базу данных: ${dbConfig.database}`);
await client.createDatabase(dbConfig.database);
console.log('База данных успешно создана.');
} catch (error) {
console.error('Ошибка при создании базы данных:', error);
}
}
- Проверяем, существует ли файл базы (
fs.existsSync()). - Если базы нет — создаём её (
client.createDatabase()). - При ошибке — логируем (catch(error)).
- Создание таблицы contacts (если её нет)
async function createContactsTable() {
try {
const attachment = await connectToDatabase();
const transaction = await attachment.startTransaction();
// ✅ Проверяем, существует ли таблица
const checkTableQuery = `
SELECT 1 FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = 'CONTACTS'
`;
const resultSet = await attachment.executeQuery(transaction, checkTableQuery);
const tableExists = (await resultSet.fetch()).length > 0;
await resultSet.close();
if (tableExists) {
console.log('Таблица "contacts" уже существует. Пропускаем создание.');
} else {
const createTableQuery = `
CREATE TABLE contacts (
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(100),
phone VARCHAR(20),
email VARCHAR(100)
)
`;
await attachment.execute(transaction, createTableQuery);
console.log('✅ Таблица "contacts" успешно создана.');
}
await transaction.commit();
} catch (error) {
console.error('❌ Ошибка при создании таблицы:', error);
}
}
- Открываем соединение и начинаем транзакцию.
- Проверяем, существует ли таблица (
RDB$RELATIONS). - Если таблицы нет — создаём её (
CREATE TABLE contacts). - Фиксируем транзакцию (
commit()).
- Экспорт функций
module.exports = { connectToDatabase, createDatabase, createContactsTable };
- Работа с контактами (models/contactModel.js):
- Динамический импорт функции для подключения к базе данных, чтобы не создавалась циклическая зависимость.
async function getDbConnection() {
const { connectToDatabase } = require('./db');
return await connectToDatabase();
}
- Получение всех контактов
async function getAllContacts() {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
const resultSet = await attachment.executeQuery(transaction, 'SELECT * FROM contacts ORDER BY id DESC');
const contacts = await resultSet.fetch();
await resultSet.close();
await transaction.commit();
return contacts;
}
executeQuery()выполняет SELECT;fetch()получает данные из РЕД База Данных.
- Поиск контакта по имени
async function searchContacts(query) {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
const resultSet = await attachment.executeQuery(transaction, 'SELECT id, name, phone, email FROM contacts WHERE name LIKE ?', [`%${query}%`]);
const contacts = await resultSet.fetch();
await resultSet.close();
await transaction.commit();
return contacts;
}
- Удаление контакта
async function deleteContact(id) {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
await attachment.execute(transaction, 'DELETE FROM contacts WHERE id = ?', [id]);
await transaction.commit();
}
- Добавление контакта
async function addContact(name, phone, email) {
try {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
await attachment.execute(transaction, 'INSERT INTO contacts (name, phone, email) VALUES (?, ?, ?)', [name, phone, email]);
await transaction.commit();
} catch (error) {
console.error('❌ Ошибка при добавлении контакта:', error);
throw error;
}
}
? в SQL-запросе предотвращает SQL-инъекции
- Экспорт функций
module.exports = { getAllContacts, addContact, deleteContact, searchContacts };
-
Стилизация фронтенда (styles.css)
Файл styles.css отвечает за оформление страницы, делая её удобной, читаемой и эстетичной.
- Общие стили страницы
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: auto;
padding: 20px;
}
- Задаёт шрифт Arial.
- Ограничивает ширину страницы (600px).
- Выравнивает по центру (margin: auto).
- Добавляет внутренние отступы (padding: 20px).
- Заголовок (h1)
h1 {
text-align: center;
}
Выравнивает заголовок по центру.
- Формы (Поиск, Добавление)
form {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
- Добавляет отступ 20px снизу.
- Выравнивает элементы формы в одну линию (display: flex).
- Добавляет отступы между элементами (gap: 10px).
- Поля ввода (input)
input {
flex: 1;
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
}
- Растягивает поле ввода (flex: 1).
- Добавляет отступ внутри (padding: 8px).
- Задаёт рамку (border: 1px solid #ccc).
- Закругляет углы (border-radius: 5px).
- Кнопки (button)
button {
padding: 8px 12px;
border: none;
cursor: pointer;
border-radius: 5px;
}
- Добавляет отступ (padding: 8px 12px).
- Убирает границы (border: none).
- Меняет курсор на «руку» (cursor: pointer).
- Закругляет углы (border-radius: 5px).
- Кнопка добавления контакта (btn-add)
.btn-add {
background-color: #28a745; /* Зеленая кнопка */
color: white;
}
- Делает кнопку зелёной (background-color: #28a745).
- Делает текст белым (color: white).
- Кнопка удаления контакта (btn-delete)
.btn-delete {
background-color: #dc3545; /* Красная кнопка */
color: white;
}
- Стили для списка контактов (ul)
ul {
list-style: none;
padding: 0;
}
- Убирает стандартные маркеры списка (list-style: none).
- Удаляет отступ (padding: 0).
- Карточки контактов (.contact-card)
.contact-card {
display: flex;
justify-content: space-between;
padding: 12px;
background: white;
border-radius: 5px;
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1);
margin-bottom: 10px;
}
- Размещает элементы в строку (display: flex).
- Выравнивает их по краям (justify-content: space-between).
- Добавляет тень (box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1)).
- Закругляет углы (border-radius: 5px).
- Добавляет отступ (padding: 12px).
- Файл contactRoutes.js (маршрутизация)
Этот файл управляет:
- выводом списка контактов (GET /);
- добавлением контакта (POST /add);
- поиском контактов (POST /search);
- удалением контактов (POST /delete/:id).
- Импорт зависимостей
const express = require('express');
const router = express.Router();
const { getAllContacts, searchContacts, deleteContact, addContact } = require('../models/contactModel');
- Создаём
routerдля обработки маршрутов. - Импортируем CRUD‑функции (
getAllContacts,addContact,searchContacts,deleteContact).
- Главная страница (GET /)
router.get('/', async (req, res) => {
try {
const contacts = await getAllContacts();
res.render('index', { contacts, searchResult: null });
} catch (error) {
res.send('❌ Ошибка загрузки контактов: ' + error.message);
}
});
- Запрашивает все контакты (
getAllContacts). - Рендерит
index.ejs, передаваяcontacts.
- Добавление контакта (POST /add)
router.post('/add', async (req, res) => {
const { name, phone, email } = req.body;
if (!name || !phone || !email) {
return res.send('❌ Заполните все поля!');
}
try {
await addContact(name, phone, email);
res.redirect('/');
} catch (error) {
res.send('❌ Ошибка при добавлении контакта: ' + error.message);
}
});
- Проверяет, заполнены ли поля.
- Вызывает
addContact. - Перенаправляет на
/после добавления.
- Поиск контакта (POST /search)
router.post('/search', async (req, res) => {
const { query } = req.body;
try {
const searchResult = await searchContacts(query);
const contacts = await getAllContacts();
res.render('index', { contacts, searchResult });
} catch (error) {
res.send('❌ Ошибка поиска: ' + error.message);
}
});
- Запрашивает контакты по имени (
searchContacts(query)). - Передаёт результаты в
index.ejs.
- Удаление контакта (POST /delete/:id)
router.post('/delete/:id', async (req, res) => {
const { id } = req.params;
try {
await deleteContact(id);
res.redirect('/');
} catch (error) {
res.send('❌ Ошибка удаления: ' + error.message);
}
});
- Получает
idконтакта из URL. - Вызывает
deleteContact(id). - Перенаправляет на
/после удаления.
- Файл index.ejs — это основной шаблон фронтенда, который:
- Отображает список контактов
- Позволяет добавлять, искать и удалять контакты
- Использует стили из styles.css
- Динамически обновляется через EJS
-
Файл index.ejs содержит:
- HTML-разметку страницы
- Форму добавления контактов
- Форму поиска контактов
- Динамический список контактов
- Подключение стилей (CSS)
-
Заголовок и мета-теги
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Контакты</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<h1>???? Контакты</h1>
- Используем HTML5 (
<!DOCTYPE html>). - Указываем кодировку UTF‑8 (поддержка русского языка).
- Задаём адаптивность (
meta viewport). - Подключаем стили (
/styles.css). - Выводим заголовок «Контакты».
- Форма добавления нового контакта
<!-- ???? Форма для добавления нового контакта -->
<form action="/add" method="post">
<input type="text" name="name" placeholder="Имя" required>
<input type="text" name="phone" placeholder="Телефон" required>
<input type="email" name="email" placeholder="Email" required>
<button type="submit" class="btn-add">➕ Добавить</button>
</form>
- Отправляет POST‑запрос на
/add. - Содержит 3 поля:
name,phone,email. - Все поля обязательны (
required). - Кнопка «Добавить» (
.btn-add) стилизована вstyles.css.
- Форма поиска контакта
- Отправляет POST‑запрос на
/search. - Ищет контакты по имени (
name LIKE %query%). - Поле
queryобязательно (required). - Кнопка «Найти» отправляет запрос.
- Динамический вывод результатов поиска
<!-- ???? Если есть результаты поиска -->
<% if (searchResult && searchResult.length > 0) { %>
<div class="search-result">
<p>???? Найдено <%= searchResult.length %> контактов:</p>
<ul>
<% searchResult.forEach(contact => { %>
<li class="contact-card">
<span><%= contact[1] %></span>
<span><%= contact[2] %></span>
<span><%= contact[3] %></span>
</li>
<% }) %>
</ul>
</div>
<% } %>
- Проверяем, есть ли
searchResult. - Если контакты найдены — отображаем их (
forEach). - Выводим: имя, телефон, email.
- Вывод списка контактов
<!-- ???? Список контактов -->
<ul>
<% contacts.forEach(contact => { %>
<li class="contact-card">
<span class="name"><%= contact[1] %></span>
<span class="phone"><%= contact[2] %></span>
<span class="email"><%= contact[3] %></span>
<form action="/delete/<%= contact[0] %>" method="post" onsubmit="return confirm('Удалить контакт?');">
<button type="submit" class="btn-delete">???? Удалить</button>
</form>
</li>
<% }) %>
</ul>
- Перебираем все контакты (
forEach). - Выводим: имя, телефон, email.
- Добавляем кнопку удаления контакта (POST
/delete/:id). - Перед удалением запрашиваем подтверждение (
confirm()).
- Подключение стилей (css)
<link rel="stylesheet" href="/styles.css">
- Файл server.js — это основной серверный файл, который:
- Запускает Express-сервер
- Инициализирует СУБД РЕД База Данных
- Настраивает шаблоны (EJS) и middleware
- Подключает маршруты (contactRoutes.js)
- Импорт зависимостей
const express = require('express');
const path = require('path');
const contactRoutes = require('./routes/contactRoutes');
const { createDatabase, createContactsTable } = require('./models/db');
express— фреймворк для обработки HTTP‑запросов.path— встроенный модуль Node.js для работы с путями.contactRoutes— маршруты изroutes/contactRoutes.js.createDatabase,createContactsTable— функции для работы с РЕД База Данных.
- Создание Express-приложения
const app = express();
const PORT = 3000;
- Создаём экземпляр Express (
app) — главный объект сервера. - Определяем порт (
PORT = 3000), на котором работает сервер.
- Инициализация базы данных
(async () => {
await createDatabase();
await createContactsTable();
})();
- Асинхронная IIFE‑функция (Immediately Invoked Function Expression).
- Вызываем
createDatabase()— создаёт базу, если её нет. - Вызываем
createContactsTable()— создаёт таблицуcontacts, если она не существует.
- Настройка шаблонов (EJS)
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
- Используем EJS как шаблонизатор (
view engine = ejs). - Указываем папку с шаблонами (
views).
- Middleware (Промежуточные обработчики)
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
- Позволяет Express обрабатывать POST‑запросы с
application/x-www-form-urlencoded. - Без этого
req.bodyбудетundefined. - Подключает папку
public/для CSS, JS и изображений (сервер отдаёт/styles.css).
- Подключение маршрутов
app.use('/', contactRoutes);
- Все маршруты (
/,/add,/search,/delete/:id) обрабатываетcontactRoutes.js. - При обращении к http://localhost:3000/ Express передаёт управление
contactRoutes.js.
- Запуск сервера
app.listen(PORT, () => {
console.log(`???? Сервер запущен на http://localhost:${PORT}`);
});
- Пример работы программы:

Заключение
В этом руководстве мы пошагово разобрали процесс создания веб-приложения на Node.js, которое взаимодействует с базой данных РЕД База Данных.
Что мы сделали?
- Подготовка окружения
- ✔ Установили Node.js, npm, РЕД Базу Данных и необходимые библиотеки.
- ✔ Настроили Visual Studio Code и конфигурацию отладки.
- Создание структуры проекта
- ✔ Разделили код на модули (
models/,routes/,config/,views/). - ✔ Использовали EJS для шаблонов и Express.js для сервера.
- Подключение к РЕД База Данных (
db.js)
- ✔ Создали базу данных и настроили автоматическое подключение.
- ✔ Добавили проверку наличия таблицы перед её созданием.
- Работа с контактами (
contactModel.js)
- ✔ Добавили CRUD‑функции:
- Добавление (addContact)
- Получение (getAllContacts)
- Поиск (searchContacts)
- Удаление (deleteContact)
- Создание API‑маршрутов (
contactRoutes.js)
- ✔ Настроили обработку HTTP‑запросов (
GET /,POST /add,POST /search,POST /delete/:id). - ✔ Использовали асинхронные функции для работы с БД.
- Запуск сервера (
server.js)
- ✔ Настроили Express‑сервер.
- ✔ Подключили шаблонизатор EJS.
- ✔ Инициализировали базу перед запуском.
- Стилизация интерфейса (
styles.css)
- ✔ Сделали страницу удобной и читаемой.
- ✔ Добавили стили для кнопок (
.btn-add,.btn-delete). - ✔ Использовали карточки контактов (
.contact-card).
Полный код программы
dbConfig.js
const path = require('path');
module.exports = {
host: 'localhost',
port: 3050,
user: 'sysdba',
password: 'masterkey',
database: path.resolve(__dirname, '../db/phonebook.fdb')
};
contactModel.js
async function getDbConnection() {
const { connectToDatabase } = require('./db');
return await connectToDatabase();
}
async function getAllContacts() {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
const resultSet = await attachment.executeQuery(transaction, 'SELECT * FROM contacts ORDER BY id DESC');
const contacts = await resultSet.fetch();
await resultSet.close();
await transaction.commit();
return contacts;
}
async function searchContacts(query) {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
const resultSet = await attachment.executeQuery(transaction, 'SELECT id, name, phone, email FROM contacts WHERE name LIKE ?', [`%${query}%`]);
const contacts = await resultSet.fetch();
await resultSet.close();
await transaction.commit();
return contacts;
}
async function deleteContact(id) {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
await attachment.execute(transaction, 'DELETE FROM contacts WHERE id = ?', [id]);
await transaction.commit();
}
async function addContact(name, phone, email) {
try {
const attachment = await getDbConnection();
const transaction = await attachment.startTransaction();
await attachment.execute(transaction, 'INSERT INTO contacts (name, phone, email) VALUES (?, ?, ?)', [name, phone, email]);
await transaction.commit();
} catch (error) {
console.error('❌ Ошибка при добавлении контакта:', error);
throw error;
}
}
module.exports = { getAllContacts, addContact, deleteContact, searchContacts };
db.js
const fs = require('fs');
const { createNativeClient, getDefaultLibraryFilename } = require('node-firebird-driver-native');
const dbConfig = require('../config/dbConfig');
const { addContact } = require('./contactModel');
const client = createNativeClient(getDefaultLibraryFilename());
async function connectToDatabase() {
try {
console.log('???? Подключение к БД:', dbConfig.database);
console.log('???? Тип переменной database:', typeof dbConfig.database);
return await client.connect(dbConfig.database, {user: dbConfig.user, password: dbConfig.password})
} catch (error) {
console.error('Ошибка подключения к базе данных:', error);
throw error;
}
}
// Создание базы данных, если её нет
async function createDatabase() {
if (fs.existsSync(dbConfig.database)) {
console.log('База данных уже существует.');
return;
}
try {
console.log(`Создаём базу данных: ${dbConfig.database}`);
await client.createDatabase(dbConfig.database);
console.log('База данных успешно создана.');
} catch (error) {
console.error('Ошибка при создании базы данных:', error);
}
}
// Создание таблицы contacts
async function createContactsTable() {
try {
const attachment = await connectToDatabase();
const transaction = await attachment.startTransaction();
// ✅ Проверяем, существует ли таблица
const checkTableQuery = `
SELECT 1 FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = 'CONTACTS'
`;
const resultSet = await attachment.executeQuery(transaction, checkTableQuery);
const tableExists = (await resultSet.fetch()).length > 0;
await resultSet.close();
if (tableExists) {
console.log('Таблица "contacts" уже существует. Пропускаем создание.');
} else {
const createTableQuery = `
CREATE TABLE contacts (
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(100),
phone VARCHAR(20),
email VARCHAR(100)
)
`;
await attachment.execute(transaction, createTableQuery);
console.log('✅ Таблица "contacts" успешно создана.');
}
await transaction.commit();
} catch (error) {
console.error('❌ Ошибка при создании таблицы:', error);
}
}
module.exports = { connectToDatabase, createDatabase, createContactsTable, addContact};
styles.css
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: auto;
padding: 20px;
background-color: #f9f9f9;
}
h1 {
text-align: center;
}
form {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
input {
flex: 1;
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
padding: 8px 12px;
border: none;
cursor: pointer;
border-radius: 5px;
}
.btn-add {
background-color: #28a745; /* Зеленая кнопка */
color: white;
}
.btn-delete {
background-color: #dc3545; /* Красная кнопка */
color: white;
}
ul {
list-style: none;
padding: 0;
}
.contact-card {
display: flex;
justify-content: space-between;
padding: 12px;
background: white;
border-radius: 5px;
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1);
margin-bottom: 10px;
}
.contact-card span {
flex: 1;
}
.contact-card form {
margin: 0;
}
contactRoutes.js
const express = require('express');
const router = express.Router();
const { getAllContacts, searchContacts, deleteContact, addContact } = require('../models/contactModel');
// ???? Главная страница — показывает список контактов
router.get('/', async (req, res) => {
try {
const contacts = await getAllContacts();
res.render('index', { contacts, searchResult: null });
} catch (error) {
res.send('❌ Ошибка подключения к базе данных: ' + error.message);
}
});
// ???? Добавление контакта
router.post('/add', async (req, res) => {
const { name, phone, email } = req.body;
if (!name || !phone || !email) {
return res.send('❌ Заполните все поля!');
}
try {
await addContact(name, phone, email);
res.redirect('/');
} catch (error) {
res.send('❌ Ошибка при добавлении контакта: ' + error.message);
}
});
// ???? Поиск контакта
router.post('/search', async (req, res) => {
const { query } = req.body;
try {
const searchResult = await searchContacts(query);
const contacts = await getAllContacts();
res.render('index', { contacts, searchResult });
} catch (error) {
res.send('❌ Ошибка поиска: ' + error.message);
}
});
// ???? Удаление контакта
router.post('/delete/:id', async (req, res) => {
const { id } = req.params;
try {
await deleteContact(id);
res.redirect('/');
} catch (error) {
res.send('❌ Ошибка удаления: ' + error.message);
}
});
module.exports = router;
index.ejs
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Контакты</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<h1>???? Контакты</h1>
<!-- ???? Форма для добавления нового контакта -->
<form action="/add" method="post">
<input type="text" name="name" placeholder="Имя" required>
<input type="text" name="phone" placeholder="Телефон" required>
<input type="email" name="email" placeholder="Email" required>
<button type="submit" class="btn-add">➕ Добавить</button>
</form>
<!-- ???? Поисковая форма -->
<form action="/search" method="post">
<input type="text" name="query" placeholder="Введите имя..." required>
<button type="submit">???? Найти</button>
</form>
<!-- ???? Если есть результаты поиска -->
<% if (searchResult && searchResult.length > 0) { %>
<div class="search-result">
<p>???? Найдено <%= searchResult.length %> контактов:</p>
<ul>
<% searchResult.forEach(contact => { %>
<li class="contact-card">
<span><%= contact[1] %></span>
<span><%= contact[2] %></span>
<span><%= contact[3] %></span>
</li>
<% }) %>
</ul>
</div>
<% } %>
<!-- ???? Список контактов -->
<ul>
<% contacts.forEach(contact => { %>
<li class="contact-card">
<span class="name"><%= contact[1] %></span>
<span class="phone"><%= contact[2] %></span>
<span class="email"><%= contact[3] %></span>
<form action="/delete/<%= contact[0] %>" method="post" onsubmit="return confirm('Удалить контакт?');">
<button type="submit" class="btn-delete">???? Удалить</button>
</form>
</li>
<% }) %>
</ul>
</body>
</html>
server.js
const express = require('express');
const path = require('path');
const contactRoutes = require('./routes/contactRoutes');
const { createDatabase, createContactsTable } = require('./models/db');
const app = express();
const PORT = 3000;
// ???? Инициализация базы данных
(async () => {
await createDatabase();
await createContactsTable();
})();
// ???? Настройка шаблонизатора EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// ???? Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
// ???? Подключение маршрутов
app.use('/', contactRoutes);
// ???? Запуск сервера
app.listen(PORT, () => {
console.log(`???? Сервер запущен на http://localhost:${PORT}`);
});
Дата последнего изменения: 07.11.2026
Если вы нашли ошибку, пожалуйста, выделите текст и нажмите Ctrl+Enter.