Node.js
Как достать данные из модуля с базой в Node.js Express
Короткий ответ: модуль с базой должен экспортировать функцию, которая возвращает данные, а Express-роут должен вызвать эту функцию через await и отправить результат в res.json. Не нужно пытаться “достать переменную” из асинхронного кода: в Node.js данные приходят позже, поэтому их передают через promise или async-функцию
Этот запрос почти всегда появляется в момент, когда код уже разделили на файлы, но логика асинхронности еще не до конца понятна. Файл db.js вроде получает данные, файл server.js хочет их показать, а в ответе получается undefined, пустой массив или promise вместо результата
Минимальная структура проекта
Сделаем простой пример
project/
db.js
server.js
package.json
Установите Express
npm init -y
npm install express
Модуль базы должен возвращать данные
Начнем без реальной базы, чтобы увидеть саму механику. Файл db.js
async function getUsers() {
return [
{ id: 1, name: "Анна" },
{ id: 2, name: "Илья" }
];
}
module.exports = {
getUsers
};
Здесь важно, что getUsers не пишет данные в глобальную переменную, а именно возвращает результат. Express сможет дождаться этой функции
Express-роут вызывает функцию через await
Файл server.js
const express = require("express");
const { getUsers } = require("./db");
const app = express();
app.get("/users", async (req, res) => {
try {
const users = await getUsers();
res.json(users);
} catch (error) {
console.error(error);
res.status(500).json({ message: "Не удалось получить пользователей" });
}
});
app.listen(3000, () => {
console.log("Сервер запущен: http://localhost:3000");
});
Запуск
node server.js
Откройте в браузере
http://localhost:3000/users
Если все сделано правильно, вы увидите JSON-массив пользователей
Пример с PostgreSQL
Если используется PostgreSQL и пакет pg, модуль может выглядеть так
npm install pg
db.js
const { Pool } = require("pg");
const pool = new Pool({
connectionString: process.env.DATABASE_URL
});
async function getUsers() {
const result = await pool.query("select id, name from users order by id");
return result.rows;
}
module.exports = {
getUsers
};
server.js остается почти таким же
const express = require("express");
const { getUsers } = require("./db");
const app = express();
app.get("/users", async (req, res) => {
try {
const users = await getUsers();
res.json(users);
} catch (error) {
console.error(error);
res.status(500).json({ message: "Ошибка базы данных" });
}
});
app.listen(3000, () => {
console.log("Сервер запущен");
});
Ключевая идея не меняется: модуль базы возвращает result.rows, а роут ждет результат через await
Почему не работает вариант с внешней переменной
Плохой пример
let users = [];
async function loadUsers() {
users = await getUsersFromDatabase();
}
loadUsers();
module.exports = { users };
На первый взгляд кажется, что можно экспортировать users. Но loadUsers работает асинхронно, и в момент экспорта массив может быть еще пустым. Даже если он заполнится позже, другой файл может уже прочитать старое значение
Правильнее экспортировать функцию
module.exports = {
getUsers
};
И каждый раз получать данные в роуте или в сервисном слое
Как передать параметр из Express в модуль базы
Например, нужно получить пользователя по id
db.js
async function getUserById(id) {
const result = await pool.query(
"select id, name from users where id = $1",
[id]
);
return result.rows[0] || null;
}
module.exports = {
getUserById
};
server.js
app.get("/users/:id", async (req, res) => {
try {
const user = await getUserById(req.params.id);
if (!user) {
res.status(404).json({ message: "Пользователь не найден" });
return;
}
res.json(user);
} catch (error) {
console.error(error);
res.status(500).json({ message: "Ошибка базы данных" });
}
});
Так роут отвечает за HTTP, а модуль базы отвечает за запрос к базе. Это нормальное разделение обязанностей
Мини-практика
Сделайте модуль db.js, который возвращает массив задач
async function getTasks() {
return [
{ id: 1, title: "Установить Node.js" },
{ id: 2, title: "Запустить Express" }
];
}
module.exports = { getTasks };
Затем создайте роут /tasks, вызовите await getTasks() и верните результат через res.json. После этого замените массив на реальный запрос к базе
Частые ошибки
Забыли await Если написать const users = getUsers(), в переменной будет promise, а не готовые данные
Экспортируют переменную вместо функции Для асинхронной базы лучше экспортировать функцию, которая возвращает результат
Смешивают Express и SQL в одном файле Для маленькой проверки это допустимо, но в нормальном проекте удобнее разделять роуты, бизнес-логику и доступ к базе
Не обрабатывают ошибку базы Если запрос упал, пользователь должен получить 500-ответ, а сервер не должен молча зависнуть
Что почитать дальше по Node.js
Если нужен общий маршрут по теме, откройте рубрику Node.js. Для соседних задач пригодятся эти разборы:
- Как в Node.js Express отдать файл по ссылке: sendFile и static
- Как запустить сервер Node.js: простой HTTP-сервер и Express
- Как получить содержимое директории и поддиректорий в Node.js Express
- Как правильно настроить маршрут в Node.js, Express и Mongoose