Гид по технологиям

CRUD API на Node и PostgreSQL

4 min read Backend Обновлено 12 Dec 2025
CRUD API на Node и PostgreSQL
CRUD API на Node и PostgreSQL

Женщина пишет на белой доске

Что такое CRUD и зачем это нужно

CRUD — это четыре базовые операции с данными: Create (создать), Read (читать), Update (обновить) и Delete (удалить). Эти операции лежат в основе большинства REST API, которые взаимодействуют с реляционными базами данных.

Краткое определение: CRUD — стандартный набор действий для управления записями в базе данных.

Требования

Перед началом убедитесь, что у вас установлены:

  • Node.js.
  • PostgreSQL (локально или удалённый сервер).
  • Базовые знания Express.js.

Совет: используйте Node версии LTS и храните конфигурацию доступа к базе в переменных окружения (см. секцию безопасности).

Шаг 1 — Создаём Express сервер

Создайте каталог проекта и перейдите в него:

mkdir notes
cd notes

Инициализируйте npm и установите зависимости:

npm init -y
npm install express

Создайте файл index.js с базовой конфигурацией сервера:

const express = require("express");
const app = express();

app.use(express.urlencoded({
  extended: true
}));

app.use(express.json());
app.listen(3000, () => console.log("Listening on port 3000"));

После этого у вас будет работающий сервер на порту 3000.

Шаг 2 — Создаём базу данных PostgreSQL

В psql выполните команду для создания базы данных notedb:

CREATE DATABASE notedb;

Проверьте список баз данных:

\l

Шаг 3 — Подключение Node к PostgreSQL

Установите клиент node-postgres:

npm install pg

В качестве хорошей практики храните подключение в отдельном файле.

Создайте db.js:

const { Client } = require("pg");
const { user, host, database, password, port } = require("./dbConfig");

const client = new Client({
  user,
  host,
  database,
  password,
  port,
});

client.connect();
module.exports = client;

Создайте файл dbConfig.js и укажите параметры подключения (замените плейсхолдеры на свои данные):

module.exports = {
  user: "{dbUser}",
  host: "{dbHost}",
  database: "notedb",
  password: "{dbPassword}",
  port: 5432,
};

Важно: в production используйте переменные окружения и не храните пароли в репозитории.

Шаг 4 — Создаём таблицу notes

Подключитесь к notedb:

\c notedb

Создайте таблицу:

CREATE TABLE notes (
  ID SERIAL PRIMARY KEY,
  note VARCHAR(255)
);

Таблица содержит автоинкрементный первичный ключ ID и текстовое поле note.

Шаг 5 — Функции CRUD (helper.js)

Вынесем логику работы с БД в отдельный файл helper.js. Сначала импортируем соединение:

const client = require("./db");

Функция для создания заметки:

const createNote = (req, res) => {
  try {
    const { note } = req.body;

    if (!note) {
      throw Error("Send note in request body");
    }

    client.query(
      "INSERT INTO notes (note) VALUES ($1)",
      [note],
      (err, data) => {
        res.status(201).json({
          error: null,
          message: "Created new note",
        });
      }
    );
  } catch (error) {
    res.status(500).json({
      error: error.message,
      message: "Failed to create new note",
    });
  }
};

Функция для получения всех заметок:

const getNotes = (req, res) => {
  try {
    client.query("SELECT * FROM notes", (err, data) => {
      if (err) throw err;

      res.status(200).json({
        err: null,
        notes: data.rows,
      });
    });
  } catch (error) {
    res.status(500).json({
      err: error.message,
      notes: null,
    });
  }
};

Функция для получения заметки по ID:

const getNoteById = (req, res) => {
  try {
    const { id } = req.params;
    client.query("SELECT * FROM notes WHERE id=$1", [id], (err, data) => {
      if (err) throw err;

      res.status(200).json({
        err: null,
        note: data.rows[0],
      });
    });
  } catch (error) {
    res.status(500).json({
      err: err.message,
      note: null,
    });
  }
};

Функция для обновления заметки по ID:

const updateNoteById = (req, res) => {
  try {
    const { id } = req.params;
    const { note } = req.body;
    client.query(
      "UPDATE notes SET note = $1 WHERE id = $2",
      [note, id],
      (err, data) => {
        if (err) throw err;

        res.status(201).json({
          err: null,
          message: "Updated note",
        });
      }
    );
  } catch (error) {
    res.status(500).json({
      err: error.message,
      message: "Failed to update note",
    });
  }
};

Функция для удаления заметки по ID:

const deleteNote = (req, res) => {
  try {
    const { id } = req.params;
    client.query("DELETE FROM notes WHERE id=$1", [id], (err, data) => {
      if (err) throw err;

      res.status(200).json({
        error: null,
        message: "Note deleted",
      });
    });
  } catch (error) {
    res.status(500).json({
      error: error.message,
      message: "Failed to delete note",
    });
  }
};

Экспортируем функции в конце helper.js:

module.exports = { createNote, getNotes, getNoteById, updateNoteById, deleteNote };

Шаг 6 — Маршруты API (index.js)

Импортируйте helper и создайте маршруты:

const db = require("./helper");

app.get("/notes", db.getNotes);
app.get("/note/:id", db.getNoteById);
app.put("/note/:id", db.updateNoteById);
app.post("/note", db.createNote);
app.delete("/note/:id", db.deleteNote);

Теперь у вас есть пять REST-эндпоинтов для управления заметками.

Проверка и отладка

Проверять можно через Postman, curl или тесты. Примеры curl-запросов:

Создать заметку:

curl -X POST -H "Content-Type: application/json" -d '{"note":"Моя заметка"}' http://localhost:3000/note

Получить все заметки:

curl http://localhost:3000/notes

Получить заметку по id:

curl http://localhost:3000/note/1

Обновить заметку:

curl -X PUT -H "Content-Type: application/json" -d '{"note":"Обновлённая заметка"}' http://localhost:3000/note/1

Удалить заметку:

curl -X DELETE http://localhost:3000/note/1

Лучшие практики и безопасное развёртывание

Important: никогда не храните пароли и строки подключения в репозитории.

Рекомендации:

  • Используйте pg.Pool вместо Client для управления соединениями в продакшене.
  • Храните конфигурацию в переменных окружения (process.env) или в секретном хранилище.
  • Всегда используйте параметризованные запросы (как в примере с $1), чтобы предотвратить SQL-инъекции.
  • Проверяйте входные данные: используйте валидацию схем (например Joi или express-validator).
  • Ограничьте CORS и доступ к API по необходимости.
  • Включите HTTPS на уровне прокси/балансировщика.
  • Лимитируйте число запросов (rate limiting) и логируйте ошибочные запросы.

Пример конфигурации через переменные окружения (рекомендация для dbConfig.js):

module.exports = {
  user: process.env.PG_USER,
  host: process.env.PG_HOST,
  database: process.env.PG_DATABASE,
  password: process.env.PG_PASSWORD,
  port: parseInt(process.env.PG_PORT, 10) || 5432,
};

Когда этот подход не подходит

Контрпримеры/ограничения:

  • Высоконагруженные приложения: для большого числа одновременных соединений нужна Pool и оптимизация запросов.
  • Сложные доменные модели: удобнее использовать ORM (например TypeORM или Sequelize) или CQRS-подход.
  • Транзакционная логика: используйте явные транзакции и тестируйте откаты.

Альтернативные подходы

  • Использовать ORM (TypeORM, Sequelize) — упрощают работу с моделями и миграциями.
  • GraphQL вместо REST — когда клиенту нужны гибкие запросы.
  • Использовать миграции (Flyway, db-migrate или миграции в ORM) для управления схемой БД.

Чек-лист по ролям

Developer:

  • Настроил .env и не закоммитил секреты.
  • Использует Pool в продакшене.
  • Написал юнит/интеграционные тесты для маршрутов.

DBA:

  • Установил резервное копирование и точки восстановления.
  • Настроил мониторинг и метрики производительности.

Tester:

  • Проверил сценарии happy-path и негативные сценарии.
  • Валидировал реакции на некорректные входные данные.

Критерии приёмки

  • Все 5 эндпоинтов возвращают ожидаемые HTTP-коды и JSON-ответы.
  • При некорректных входных данных API возвращает корректные ошибки (4xx/5xx).
  • Данные сохраняются, обновляются и удаляются в базе данных как ожидается.
  • Пароли и секреты не попали в репозиторий.

Тест-кейсы (минимум)

  1. Создание заметки
    • Вход: валидный JSON {“note”:”текст”}
    • Ожидание: 201 Created, сообщение об успешном создании.
  2. Получение списка
    • Ожидание: 200 OK, массив заметок.
  3. Получение по несуществующему ID
    • Ожидание: 200 OK, note: null или 404 с описанием (вариация зависит от реализации).
  4. Обновление note с пустым телом
    • Ожидание: 500 или 400 с сообщением об ошибке валидации.
  5. Удаление
    • Ожидание: 200 OK и подтверждение удаления.

Короткие подсказки и шпаргалка

  • Используйте параметр $1, $2 и передаваемые массивы для безопасных запросов.
  • В тестах создавайте отдельную тестовую БД или используйте транзакции с откатом.
  • Для миграций используйте специальный инструмент, чтобы синхронизировать схему между окружениями.

Итог

Вы создали минимальный CRUD API на Express и PostgreSQL. Этот шаблон легко расширяется: добавьте аутентификацию, валидацию, миграции и мониторинг перед выпуском в продакшен. Начните с этого каркаса и улучшайте архитектуру по мере возрастания требований.

Notes: можно управлять базой из GUI через pgAdmin для удобства администрирования и обзора данных.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство