JavaScript и PostgreSQL: полное руководство по интеграции на Node.js

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Зачем JavaScript дружит с PostgreSQL?

Если вы пишете на JavaScript (особенно на Node.js) и до сих пор используете MySQL или, упаси боже, SQLite в продакшене, — вы теряете деньги и нервы. PostgreSQL — это не просто "еще одна база данных". Это швейцарский нож для данных, который умеет всё: от сложных JOIN до JSON-документов, полнотекстового поиска и гео-запросов. А связка JavaScript + PostgreSQL — это современный стандарт для веб-приложений, микросервисов и аналитики.

В этом руководстве мы разберем всё, что нужно знать Node.js-разработчику: от установки драйвера до транзакций, миграций и оптимизации. Никакой воды — только код и практика.

Шаг 1: Установка и настройка драйвера pg

Для работы с PostgreSQL из JavaScript используется пакет pg — официальный клиент. Устанавливается он за секунду:

npm install pg

Теперь создаем подключение. Есть два подхода: Client (одно соединение) и Pool (пул соединений). Для продакшена всегда используйте Pool — он управляет переиспользованием соединений и не кладет базу при нагрузке.

const { Pool } = require('pg');

const pool = new Pool({
  user: 'your_user',
  host: 'localhost',
  database: 'your_db',
  password: 'your_password',
  port: 5432,
  max: 20,        // макс. соединений в пуле
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

// Тест подключения
pool.query('SELECT NOW()')
  .then(res => console.log('✅ Подключено:', res.rows[0].now))
  .catch(err => console.error('❌ Ошибка:', err));

Важно: Никогда не хардкодьте пароли. Используйте переменные окружения (process.env.DATABASE_URL) или менеджер секретов.

Шаг 2: CRUD — основа основ

Любое приложение начинается с CRUD (Create, Read, Update, Delete). Разберем каждую операцию на JavaScript с PostgreSQL.

Создание таблицы (DDL)

const createTable = async () => {
  const query = `
    CREATE TABLE IF NOT EXISTS users (
      id SERIAL PRIMARY KEY,
      name VARCHAR(100) NOT NULL,
      email VARCHAR(255) UNIQUE NOT NULL,
      age INTEGER CHECK (age > 0),
      created_at TIMESTAMP DEFAULT NOW()
    );
  `;
  await pool.query(query);
  console.log('✅ Таблица users создана');
};

createTable();

INSERT — добавление данных

const addUser = async (name, email, age) => {
  // Используем параметризованный запрос — защита от SQL-инъекций!
  const query = `
    INSERT INTO users (name, email, age)
    VALUES ($1, $2, $3)
    RETURNING *;
  `;
  const values = [name, email, age];
  const res = await pool.query(query, values);
  console.log('✅ Добавлен пользователь:', res.rows[0]);
  return res.rows[0];
};

// Пример вызова
addUser('Анна Петрова', 'anna@example.com', 28);

SELECT — чтение с фильтрацией

const getUsers = async (minAge = 0) => {
  const query = 'SELECT * FROM users WHERE age > $1 ORDER BY created_at DESC';
  const res = await pool.query(query, [minAge]);
  console.log(`📊 Найдено пользователей: ${res.rowCount}`);
  return res.rows;
};

getUsers(25).then(users => console.log(users));

UPDATE — обновление записи

const updateUserEmail = async (id, newEmail) => {
  const query = `
    UPDATE users
    SET email = $2, updated_at = NOW()
    WHERE id = $1
    RETURNING *;
  `;
  const res = await pool.query(query, [id, newEmail]);
  if (res.rowCount === 0) {
    console.log('⚠️ Пользователь не найден');
    return null;
  }
  console.log('✅ Email обновлен:', res.rows[0]);
  return res.rows[0];
};

DELETE — удаление

const deleteUser = async (id) => {
  const query = 'DELETE FROM users WHERE id = $1 RETURNING *';
  const res = await pool.query(query, [id]);
  console.log(`🗑️ Удален пользователь: ${res.rowCount > 0 ? res.rows[0].name : 'не найден'}`);
};

Шаг 3: Продвинутые техники

Просто CRUD — это скучно. Давайте посмотрим, как JavaScript и PostgreSQL работают вместе в реальных сценариях.

Транзакции — спасаем данные

Когда нужно выполнить несколько запросов атомарно (всё или ничего), используем транзакции.

const transferMoney = async (fromId, toId, amount) => {
  const client = await pool.connect(); // получаем клиент из пула
  try {
    await client.query('BEGIN'); // начинаем транзакцию

    // Списание
    await client.query(
      'UPDATE accounts SET balance = balance - $1 WHERE id = $2 AND balance >= $1',
      [amount, fromId]
    );
    // Зачисление
    await client.query(
      'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
      [amount, toId]
    );

    await client.query('COMMIT'); // всё ок — фиксируем
    console.log('✅ Перевод выполнен');
  } catch (err) {
    await client.query('ROLLBACK'); // ошибка — откат
    console.error('❌ Ошибка перевода, откат:', err.message);
  } finally {
    client.release(); // обязательно возвращаем клиент в пул
  }
};

Работа с JSON в PostgreSQL

PostgreSQL поддерживает JSONB — бинарный JSON с индексацией. Это идеально для гибких схем.

const addProduct = async (product) => {
  const query = `
    INSERT INTO products (name, metadata)
    VALUES ($1, $2::jsonb)
    RETURNING *;
  `;
  const metadata = JSON.stringify({
    color: product.color,
    weight: product.weight,
    tags: product.tags || []
  });
  const res = await pool.query(query, [product.name, metadata]);
  return res.rows[0];
};

// Поиск по JSON-полю
const findProductsByTag = async (tag) => {
  const query = "SELECT * FROM products WHERE metadata->'tags' ? $1";
  const res = await pool.query(query, [tag]);
  return res.rows;
};

Миграции баз данных

Ручное создание таблиц — путь в ад. Используйте миграции. Популярные инструменты для Node.js и PostgreSQL: Knex.js или node-pg-migrate.

Пример миграции на Knex.js:

// Файл: migrations/20231001_create_users.js
exports.up = function(knex) {
  return knex.schema.createTable('users', (table) => {
    table.increments('id').primary();
    table.string('name', 100).notNullable();
    table.string('email').unique().notNullable();
    table.integer('age').checkPositive();
    table.timestamps(true, true);
  });
};

exports.down = function(knex) {
  return knex.schema.dropTableIfExists('users');
};

Запуск: npx knex migrate:latest

Блоги

Рекомендаций по книгам