В этой статье я расскажу, как писал самый простой сервер для общения со старой базой данных Firebird.

Суть следующая: имеется старая база данных 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. Должно получиться что-то похожее:

Далее в папке src создаем две папки: config и db. А в папке src создаем файл index.js

Далее устанавливаем пакеты: express и cors
npm install express
npm install cors
Должна появиться папка node_modules и файл package-lock.json

Заодно, чтобы постоянно не перезапускать сервер вручную, а при любых изменениях он смог сам перезапускаться, поставим пакет 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, но им буду получать определенный "продукт". Так называемую определенную карточку.
Объявляю 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, и получаю вот так. Отлично.

Следующий метод 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 нового продукта } }); });


Далее метод 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} успешно обновлена.` }); // Отправляем успешный ответ после успешного обновления } }); });

Последний метод, который я написал, это 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} не существует. Возможно, она уже была удалена`); } }); });
Выглядит это вот так:


В самом конце файла 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, который создает тестовую базу данных, которая используется в данном примере. Просто для себя.

CREATE TABLE PRODUCTS ( ID_PROD INTEGER GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR(100), ABOUT VARCHAR(100), CONSTRAINT PK_PRODUCTS PRIMARY KEY (ID_PROD) );
Чтобы создать таблицу, нужно просто выполнить данный скрипт в БД.

