В этой статье я расскажу, как писал самый простой сервер для общения со старой базой данных 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)
);
Чтобы создать таблицу, нужно просто выполнить данный скрипт в БД.