В этой статье я расскажу, как писал самый простой сервер для общения со старой базой данных Firebird.
![](https://habrastorage.org/getpro/habr/upload_files/016/817/47e/01681747e81042c48d3b705c9ef54d27.jpg)
Суть следующая: имеется старая база данных Firebird 3.0, которая крутится на сервере. Нужно написать backend, который будет общаться с данной базой.
Не судите строго, так как это мой первый опыт написания backend в принципе.
Итак, всё что изначально имелось у меня, это база данных. Для простоты я буду обозначать ее DB
. Пропустим шаги установки NodeJS. Создаем папку проекта, инициализируем проект npm init
. Данные указываем любые. После инициализации нужно добавить строку "type": "module"
в файле package.json.
{
"type": "module",
"name": "nodejs-server-firebird",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Устанавливаем в эту же папку node-firebird
командой:
npm install node-firebird
С помощью данного пакета мы сможем подключиться к базе.
В корне папки проекта создаем папку src. Должно получиться что-то похожее:
![Структура проекта Структура проекта](https://habrastorage.org/getpro/habr/upload_files/163/5d8/a7c/1635d8a7ce5470e43d3a58373411df5b.png)
Далее в папке src создаем две папки: config и db. А в папке src создаем файл index.js
![](https://habrastorage.org/getpro/habr/upload_files/d60/9c0/8a5/d609c08a5efcdd1ddb08110d39189e77.png)
Далее устанавливаем пакеты: express
и cors
npm install express
npm install cors
Должна появиться папка node_modules
и файл package-lock.json
![](https://habrastorage.org/getpro/habr/upload_files/d3b/af3/325/d3baf3325894b000b082197a4402b988.png)
Заодно, чтобы постоянно не перезапускать сервер вручную, а при любых изменениях он смог сам перезапускаться, поставим пакет nodemon
npm install nodemon
Создаю новый файл database.js в папке config. Он будет отвечать за соединение с базой. Здесь указываю все данные базы. А также пишу функцию, которая соединяется с базой данных.
import firebird from "node-firebird";
const dbOptions = {
host: 'localhost',
port: 3050,
database: 'C:\\Users\\user\\Documents\\nodejs-server-firebird\\DB.DB',
user: 'SYSDBA',
password: 'masterkey',
lowercase_keys: true,
role: null,
pageSize: 4096
};
function executeQuery(ssql, params, callback){
firebird.attach(dbOptions, function(err, db) {
if (err) {
return callback(err, []);
}
db.query(ssql, params, function(err, result) {
db.detach();
if (err) {
return callback(err, []);
} else {
return callback(undefined, result);
}
});
});
}
export {executeQuery};
Все запросы буду писать в ранее созданном файле index.js. Импортирую необходимые модули.
import express from "express";
import cors from "cors";
import { executeQuery } from "./config/database.js";
const app = express();
app.use(express.json());
app.use(cors());
Далее пишу первый метод GET, который будет получать все данные из таблицы. Заодно добавлю условие на фильтр определенных id.
app.get("/products", (req, res) => {
try {
const { id_prod } = req.query; // Получаем параметр id_prod из запроса
let ssql = "SELECT * FROM PRODUCTS WHERE ID_PROD > 0"; // Формируем SQL-запрос
const filter = [];
if (id_prod) {
ssql += " AND ID_PROD LIKE ?"; // Добавляем условие поиска по id_prod
filter.push(`%${id_prod}%`);
}
// Выполняем SQL-запрос с использованием функции executeQuery
executeQuery(ssql, filter, (err, result) => {
if (err) {
res.status(500).json({ error: err.message }); // Обрабатываем ошибку и отправляем клиенту сообщение об ошибке
} else if (result.length === 0) {
res.status(404).json({ error: "Ничего не найдено" }); // Добавляем статус 404, если результат пустой
} else {
res.status(200).json(result); // Отправляем клиенту результат запроса
}
});
} catch (error) {
res.status(500).json({ error: error.message }); // Обрабатываем исключение и отправляем клиенту сообщение об ошибке
}
});
Получилось вот так:
![Методом GET получаю все данные Методом GET получаю все данные](https://habrastorage.org/getpro/habr/upload_files/f02/375/3e6/f023753e6fdd5dccd9164235d1ef848c.png)
При использовании фильтра, получаем определенный объект:
![Использую фильтр в методе GET Использую фильтр в методе GET](https://habrastorage.org/getpro/habr/upload_files/2c2/9c0/7bf/2c29c07bf4f95909407f19f0ae957c52.png)
Следующий метод будет тоже GET, но им буду получать определенный "продукт". Так называемую определенную карточку.
Объявляю id и получаем его из параметров запроса. А потом выбираю продукт с заданным id.
// Получение одного продукта по id
app.get("/products/:id", (req, res) => {
const id = req.params.id; // Получаем id из параметров запроса
const ssql = "SELECT * FROM PRODUCTS WHERE ID_PROD = ?"; // Используем подготовленный запрос
// Выполнение SQL-запроса с использованием функции executeQuery
executeQuery(ssql, [id], (err, result) => {
if (err) {
console.error("Ошибка при выполнении запроса:", err);
res.status(500).send("Internal Server Error"); // Отправляем ошибку сервера в случае ошибки SQL-запроса
} else if (result.length === 0) {
res.status(404).send("Product not found"); // Отправляем сообщение о том, что продукт не найден, если результат пустой
} else {
res.send(result[0]); // Отправляем первый найденный продукт в ответ на успешный запрос
}
});
});
Теперь нужно явно указать в ссылке id объекта, например /products/9. Отправляю запрос в Postman, и получаю вот так. Отлично.
![](https://habrastorage.org/getpro/habr/upload_files/2fe/8cd/a71/2fe8cda71b9e2ec71680b6d776d8545d.png)
Следующий метод POST, которым будет создавать новые объекты в базе.
// Добавление нового продукта
app.post("/products/new", (req, res) => {
const ssql = "INSERT INTO PRODUCTS(NAME, ABOUT) VALUES (?, ?) RETURNING ID_PROD"; // SQL-запрос для вставки нового продукта и получения его ID
// Выполнение SQL-запроса с использованием функции executeQuery и данными из тела запроса (req.body)
executeQuery(ssql, [req.body.NAME, req.body.ABOUT], (err, result) => {
if (err) {
if (err.code === "ER_DUP_ENTRY") {
res.status(409).json({ error: "Запись уже существует" }); // Обработка ошибки дублирования записи
} else if (err.code === "ECONNREFUSED") {
res.status(502).json({ error: "Сервер не может установить соединение с базой данных" }); // Обработка ошибки соединения с базой данных
} else {
console.error("Ошибка при выполнении запроса:", err);
res.status(500).json({ error: "Произошла ошибка при добавлении продукта" }); // Отправка общей ошибки сервера
}
} else {
res.status(201).json("Новый продукт добавлен, его id " + result.id_prod); // Отправка успешного ответа с ID нового продукта
}
});
});
![Отправляю запрос в POSTMAN Отправляю запрос в POSTMAN](https://habrastorage.org/getpro/habr/upload_files/b29/ef1/ad9/b29ef1ad91b3e28fc68d671ed8bf49fd.png)
![Смотрю, появилась строка в базе Смотрю, появилась строка в базе](https://habrastorage.org/getpro/habr/upload_files/0e1/1b9/9ac/0e11b99ac97a5aacd425ea4b9e035f76.png)
Далее метод PUT, которым буду обновлять определенную запись.
// Обновление информации о продукте по его ID
app.put("/products/:id_prod/update", function (req, res) {
const id_prod = req.params.id_prod; // Получаем ID продукта из параметров запроса
const { name, about } = req.body; // Получаем данные для обновления из тела запроса
let ssql = 'UPDATE PRODUCTS SET name = ?, about = ? WHERE ID_PROD = ?'; // SQL-запрос для обновления информации о продукте
// Выполняем SQL-запрос с использованием функции executeQuery
executeQuery(ssql, [name, about, id_prod], function (err, result) {
if (err) {
console.error(err);
return res.status(500).json({ error: 'Произошла ошибка при обновлении записи.' }); // Отправляем ошибку сервера при возникновении ошибки SQL-запроса
} else {
return res.status(200).json({ message: `Запись с id_prod: ${id_prod} успешно обновлена.` }); // Отправляем успешный ответ после успешного обновления
}
});
});
![Отправляю запрос на обновление 21 строки Отправляю запрос на обновление 21 строки](https://habrastorage.org/getpro/habr/upload_files/760/a20/9bd/760a209bdebd1f2ec51acbd928bd7a93.png)
Последний метод, который я написал, это DELETE. Изначально отправляется запрос по id_prod, который проверяет, есть ли строка в базе, если ее нет, тогда падает ошибка что ее нет. Если строка все таки есть, тогда она удаляется.
// Обработчик HTTP DELETE-запроса для удаления записи по ID_PROD
app.delete("/products/:id_prod", function (req, res) {
const id_prod = req.params.id_prod;
const selectQuery = "SELECT ID_PROD FROM PRODUCTS WHERE ID_PROD = ?";
// Выполняем запрос на выборку записи перед удалением
executeQuery(selectQuery, [id_prod], function (err, result) {
if (err) {
return res.status(500).send('Ошибка выполнения запроса на выборку: ' + err.message);
}
// Если запись с указанным ID_PROD найдена
if (result.length > 0) {
const deleteQuery = "DELETE FROM PRODUCTS WHERE ID_PROD = ?";
// Выполняем запрос на удаление записи
executeQuery(deleteQuery, [id_prod], function (err, result) {
if (err) {
return res.status(500).send('Ошибка удаления: ' + err.message);
}
// Возвращаем успешный статус и сообщение об успешном удалении
return res.send(`Удаление записи с ID_PROD=${id_prod} выполнено успешно`);
});
} else {
// Если запись не найдена, возвращаем статус 404 и сообщение об отсутствии записи
return res.status(404).send(`Запись с указанным ID_PROD ${id_prod} не существует. Возможно, она уже была удалена`);
}
});
});
Выглядит это вот так:
![Первый раз отправляем запрос, строка существует, значит удаление выполнено успешно. Первый раз отправляем запрос, строка существует, значит удаление выполнено успешно.](https://habrastorage.org/getpro/habr/upload_files/9aa/b18/2f8/9aab182f87356b4dce5774fe047df09b.png)
![Повторно отправляем этот же запрос. Приходит 404 статус. Повторно отправляем этот же запрос. Приходит 404 статус.](https://habrastorage.org/getpro/habr/upload_files/d87/4f8/5aa/d874f85aac3f2e11d75e8f999417de97.png)
В самом конце файла index.js нужно добавить прослушивание порта 3000 для сервера.
app.listen(3000, function () {
console.log("Server is running on port 3000");
});
Теперь остается запустить сервер в консоли командой:
nodemon src/index.js
Видим лог что сервер запущен:
$ nodemon src/index.js
[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node src/index.js`
Server is running on port 3000
БОНУС: В папку db еще можно положить скрипт для БД Firebird, который создает тестовую базу данных, которая используется в данном примере. Просто для себя.
![](https://habrastorage.org/getpro/habr/upload_files/1ae/e10/82b/1aee1082be2803f0903f37270b41fe5e.png)
CREATE TABLE PRODUCTS (
ID_PROD INTEGER GENERATED BY DEFAULT AS IDENTITY,
NAME VARCHAR(100),
ABOUT VARCHAR(100),
CONSTRAINT PK_PRODUCTS PRIMARY KEY (ID_PROD)
);
Чтобы создать таблицу, нужно просто выполнить данный скрипт в БД.
![](https://habrastorage.org/getpro/habr/upload_files/5bd/7e0/44a/5bd7e044a2d4793f97ddec7d3b845171.png)