Привет, Хабр!

Сегодня мы продемонстрируем как создавать back-end приложения на Node.js c PostqreSQL. В качестве примера создадим простейший back-end на Node.js с использованием PostgreSQL.

Почему именно Node.js и PostgreSQL? Node.js имеет хорошую скорость и асинхронность, а PostgreSQL, в свою очередь, является мощной и надежной СУБД.

Вместе они создают идеальный тандем для создания качественных приложений.

Создание и настройка проекта

Все будем делать на Винде.

Установим

Node.Js

Переходим на официальный сайт Node.js и скачиваем установочный файл для Windows.

Далее запускаем установочный файл и следуйте инструкциям мастера установки.

Здесь еще важно, чтобы был выбран флажок для установки npm вместе с Node.js.

После завершения установки открываем командную строку и вводим node -v и npm -v, чтобы проверить успешность установки.

PostgreSQL

Скачиваем PostgreSQL с официального сайта. Установка там достаточна простая, но важно проверить, что включены pgAdmin и psql.

Далее создаем саму базы данных и юзера.

Открываем командную строку или терминал.

Входим в консоль PostgreSQL от имени суперпользователя (обычно postgres):

sudo -i -u postgres
psql

Создаем новую БД:

CREATE DATABASE mydatabase;

Создайте нового пользователя с паролем:

CREATE USER myuser WITH ENCRYPTED PASSWORD 'qwerty';

Даем пользователю права на созданную БД:

GRANT ALL PRIVILEGES ON DATABASE mydatabase TO myuser;

Выходим из консоли PostgreSQL командой \q.

Инициализация нового проекта Node.js

Создадим новую директорию для проекта:

mkdir my-node-project
cd my-node-project

Инициализируем проект с помощью npm:

npm init -y

Это создаст файл package.json, который будет содержать основную инфу о проекте, включая зависимости и скрипты для запуска проекта.

Установим необходимые пакеты для работы с Express и PostgreSQL:

npm install express pg dotenv
  • Express: фреймворк для создания серверных приложений на Node.js.

  • pg: оф. клиент для PostgreSQL.

  • dotenv: для управления переменными окружения.

Организуем проект таким образом, чтобы было легко управлять и расширять его:

my-node-project/
│
├── src/
│   ├── routes/
│   │   └── index.js
│   ├── controllers/
│   │   └── userController.js
│   ├── models/
│   │   └── userModel.js
│   ├── config/
│   │   └── db.js
│   └── app.js
├── .env
├── package.json
└── README.md
  • routes: маршруты приложения.

  • controllers: логика обработки запросов.

  • models: модели базы данных.

  • config: конфиг, который включает настройки подключения к БД.

  • app.js: главный файл приложения.

Подключение к базе данных

Создадим файл конфигурации для подключения к БД, например, db.js:

const { Pool } = require('pg');
require('dotenv').config();

const pool = new Pool({
  user: process.env.DB_USER,
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  password: process.env.DB_PASSWORD,
  port: process.env.DB_PORT,
});

module.exports = pool;

Добавляем переменные окружения в файл .env:

DB_USER=yourusername
DB_HOST=localhost
DB_NAME=yourdatabase
DB_PASSWORD=yourpassword
DB_PORT=5432

Создаем тестовый запрос в app.js, чтобы убедиться, что соединение с базой данных установлено:

const express = require('express');
const pool = require('./config/db');

const app = express();

pool.query('SELECT NOW()', (err, res) => {
  if(err) {
    console.error('Error connecting to the database', err.stack);
  } else {
    console.log('Connected to the database:', res.rows);
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Создание API с использованием Express

Создадим файл маршрутов index.js и определим базовые маршруты:

const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router.get('/users', userController.getUsers);
router.post('/users', userController.createUser);
router.put('/users/:id', userController.updateUser);
router.delete('/users/:id', userController.deleteUser);

module.exports = router;

В контроллере userController.js описываем логику взаимодействия с базой данных:

const pool = require('../config/db');

exports.getUsers = async (req, res) => {
  try {
    const result = await pool.query('SELECT * FROM users');
    res.status(200).json(result.rows);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

exports.createUser = async (req, res) => {
  const { name, email } = req.body;
  try {
    const result = await pool.query('INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *', [name, email]);
    res.status(201).json(result.rows[0]);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

exports.updateUser = async (req, res) => {
  const { id } = req.params;
  const { name, email } = req.body;
  try {
    const result = await pool.query('UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *', [name, email, id]);
    res.status(200).json(result.rows[0]);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

exports.deleteUser = async (req, res) => {
  const { id } = req.params;
  try {
    await pool.query('DELETE FROM users WHERE id = $1', [id]);
    res.status(204).send();
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

Юзаем middleware для валидации данных и обработки ошибок:

const express = require('express');
const app = express();
const routes = require('./routes');
const { body, validationResult } = require('express-validator');

app.use(express.json());

app.post('/users', 
  body('email').isEmail(),
  body('name').notEmpty(),
  (req, res, next) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    next();
  },
  routes
);

app.use('/api', routes);

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Тестирование и отладка

Теперь, когда API создан и подключен к базе данных, можно и нужно начинать откладывать и тестировать.

Postman хорошо справится с этой задачей.

  1. Создаем запрос в Postman:

    • Запускаем Postman и создаем новый запрос.

    • Вводим URL API, например, http://localhost:3000/api/users.

    • Выбираем метод запроса: GET, POST, PUT, DELETE.

    • Если запрос требует тела, например, POST или PUT, добавляемJSON-данные в разделе Body.

  2. Отправка запроса и проверка ответа:

    • Нажимаем "Send" и проверяемответ в нижней части окна Postman

Можно реализовать логирование

Установим библиотеку winston для логирования:

npm install winston

Создадим файл logger.js для настройки логирования:

const { createLogger, format, transports } = require('winston');

const logger = createLogger({
  level: 'info',
  format: format.combine(
    format.timestamp(),
    format.json()
  ),
  transports: [
    new transports.Console(),
    new transports.File({ filename: 'error.log', level: 'error' }),
    new transports.File({ filename: 'combined.log' })
  ],
});

module.exports = logger;

Импортируем и используем логер в app.js и других файлах:

const logger = require('./logger');

app.use((req, res, next) => {
  logger.info(`${req.method} ${req.url}`);
  next();
});

app.use((err, req, res, next) => {
  logger.error(err.message);
  res.status(500).send('Something broke!');
});

Теперь мы уже создали готовый и простейший back-end на Node.js с использованием PostgreSQL, а также подключили логирование и Postman. Кстати, сюда еще можно добавить модульное тестирование с помощью библиотеки Jest.

Cоздавать back-end на Node.js с использованием PostgreSQL позволяет воспользоваться всеми фичами этих технологий!


В заключение напомню о ближайших открытых уроках:

  • 18 июля: Дженерики в Go. На вебинаре вы узнаете механизмы обобщенного программирования с использованием дженериков. Мы рассмотрим внутренние механизмы работы дженериков в Go, а также примеры использования. Запись по ссылке

  • 25 июля: Как сделать быстрорастущий сервис с помощью трейсинга? На вебинаре мы наглядно рассмотрим работу сервиса под нагрузкой и найдем запрос с помощью трейсинга. Покажем кейсы, когда уже есть логирование. Запись по ссылке

Комментарии (1)


  1. olku
    17.07.2024 18:30

    А как быть со схемой, миграциями, сущностями? Все делать вручную это не просто даже в метриках cognitive complexity. Кодовая база будет распухать, вероятность ручных ошибок тоже.
    Бросьте в меня ссылкой на туториал правильной готовки DDD в NestJS, пожалуйста.