CRUD API на Node.js и PostgreSQL
Краткие определения:
- CRUD — стандартный набор операций с данными: Create (создать), Read (читать), Update (обновить), Delete (удалить).
- Express — минималистичный фреймворк для Node.js для создания HTTP-серверов.
- PostgreSQL — реляционная база данных с открытым исходным кодом.
Что вы получите из этого руководства
- Полный рабочий пример проекта: настройка Express, подключение к PostgreSQL через pg, функции CRUD и маршруты.
- Практические советы по обработке ошибок, безопасности и тестированию.
- Контрольный список для разработчика, администратора БД и тестировщика.
Предварительные требования
- Установлен Node.js.
- Установлен PostgreSQL (локально или доступен по сети).
- Базовое знакомство с Express.js и SQL.
Важно: в примерах используется порт 3000 и база данных notedb. Замените значения на свои в конфигурации.
Шаг 1 — Создаём Express-сервер
Создайте новую папку и инициализируйте npm:
mkdir notes
cd notes
npm init -yУстановите Express:
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"));Этот код создаёт HTTP-сервер, который слушает порт 3000 и умеет читать JSON в теле запроса.
Шаг 2 — Создаём базу данных PostgreSQL
В psql выполните:
postgres=# CREATE DATABASE notedb;Проверьте список баз:
postgres=# \lЗатем подключитесь к базе notedb:
postgres=# \c notedbСоздайте таблицу notes:
notedb=# CREATE TABLE notes (
ID SERIAL PRIMARY KEY,
note VARCHAR(255)
);Эта таблица содержит автоинкрементный идентификатор и текст заметки.
Шаг 3 — Подключаем Node к PostgreSQL
Установите клиент для PostgreSQL:
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,
};Замените {dbUser}, {dbHost}, {dbPassword} на реальные значения. Если PostgreSQL запущен локально, host обычно “localhost”.
Совет по безопасности: не храните реальные пароли в репозитории. Используйте .env и библиотеку dotenv или переменные окружения.
Шаг 4 — Реализуем функции 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: error.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",
});
}
};И экспортируем функции:
module.exports = { createNote, getNotes, getNoteById, updateNoteById, deleteNote };Примечание: в коде выше используется callback-стиль client.query. Альтернатива — использовать async/await с client.query(…).then(…) или pool.query и промисы для лучшей читаемости и обработки ошибок.
Шаг 5 — Создаём маршруты API в index.js
В 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);Теперь API готов к запросам. Примеры запросов можно отправлять через curl, Postman или встроенные тесты.
Рекомендации по структуре проекта и масштабированию
- Разделяйте слои: маршруты, контроллеры (helper), доступ к данным (db) и конфигурация.
- Для реального приложения используйте connection pool (pg.Pool) вместо одиночного клиента.
- Введите миграции (например, с помощью node-pg-migrate или Flyway) для версионирования схемы БД.
Ошибки и отладка — что проверять
- Неправильные данные в dbConfig.js: host, user, password.
- Порт занят: если порт 3000 уже используется, поменяйте на другой.
- Проблемы с разрешениями пользователя PostgreSQL.
- SQL-инъекции: всегда используйте параметризованные запросы ($1, $2). В примере это сделано.
Important: клиентские ошибки (400-е) лучше обрабатывать отдельно и возвращать осмысленные сообщения. В примере общая обработка ошибок возвращает 500.
Безопасность и эксплуатация
- Не храните секреты в репозитории. Используйте переменные окружения.
- Ограничьте права пользователя БД: давайте минимальные права (INSERT/SELECT/UPDATE/DELETE) для этой схемы.
- В продакшене используйте TLS для соединения с БД, если она удалённая.
- Валидация входных данных: проверяйте длину, формат и типы полей.
Тестирование и критерии приёмки
Критерии приёмки:
- POST /note создаёт запись с кодом 201 и сообщением “Created new note”.
- GET /notes возвращает массив заметок и код 200.
- GET /note/:id возвращает объект заметки с кодом 200 или 404, если не найдено.
- PUT /note/:id обновляет заметку и возвращает код 201.
- DELETE /note/:id удаляет заметку и возвращает код 200.
Пример тестов (на уровне поведения):
- Проверить создание с пустым телом — ожидается 500 и понятная ошибка.
- Проверить создание и затем получение по ID — данные должны совпадать.
- Проверить обновление несуществующего ID — ожидается 200 (но полезно вернуть 404). Рассмотрите улучшение возврата статусов.
Разные подходы и альтернативы
- Вместо callbacks используйте async/await:
const createNoteAsync = async (req, res) => {
try {
const { note } = req.body;
if (!note) return res.status(400).json({ error: 'note required' });
await client.query('INSERT INTO notes (note) VALUES ($1)', [note]);
res.status(201).json({ message: 'Created new note' });
} catch (e) {
res.status(500).json({ error: e.message });
}
};- Для более сложных приложений используйте ORM (TypeORM, Sequelize) или query builder (Knex).
Mini-методология разработки API
- Спроектируйте контракты (контент ответов, коды статуса).
- Напишите миграции и инициализацию БД.
- Реализуйте минимальные маршруты и контроллеры.
- Добавьте валидацию и обработку ошибок.
- Напишите интеграционные тесты.
- Разверните на staging и прогоните сценарии.
Чек-лист по ролям
Разработчик:
- Разделены слои (routes/controllers/db)
- Есть обработка ошибок
- Есть unit/интеграционные тесты
Администратор БД:
- Настроены права пользователя
- Сделаны резервные копии
- Включён мониторинг доступности БД
Тестировщик:
- Тесты на все маршруты
- Тесты на неправильные тела запросов
- Тест на конкурентные запросы
Решение типичных проблем — дерево решений
flowchart TD
A[API не отвечает] --> B{Сервер запущен?}
B -- Нет --> C[Запустить Node.js]
B -- Да --> D{Порт занят?}
D -- Да --> E[Изменить порт или убить процесс]
D -- Нет --> F{Подключение к БД установлено?}
F -- Нет --> G[Проверить dbConfig и логи PostgreSQL]
F -- Да --> H[Проверить маршруты и логи приложения]Когда этот подход не подходит
- Проект с очень сложными бизнес-правилами: предпочтительнее слои домена и ORM.
- Требуется горизонтальное масштабирование с множеством соединений к БД — используйте пул и репликацию.
Совместимость и миграция
- При миграции схемы применяйте миграции с версионностью.
- Для zero-downtime обновлений избегайте удаления полей — сначала добавляйте новые, затем переключайте код.
Полезные команды и сниппеты
- Инициализация npm: npm init -y
- Установка модулей: npm install express pg
- Пример curl для создания заметки:
curl -X POST http://localhost:3000/note -H "Content-Type: application/json" -d '{"note":"текст заметки"}'Итог
Вы получили минимальную, но рабочую реализацию CRUD API на Node.js с PostgreSQL. Для продакшена доработайте обработку ошибок, валидацию, безопасность, миграции и мониторинг. Начните с компактного кода, затем итеративно улучшайте отдельные слои.
Ключевые рекомендации:
- Используйте параметризованные запросы.
- Не храните секреты в коде.
- Переходите на async/await и pool для стабильности.
Краткая сводка в конце:
- Сервер Express + pg для подключения к PostgreSQL.
- helper.js содержит CRUD-функции.
- index.js регистрирует маршруты.
- Тесты и права БД — критичны для надёжности.
1-line глоссарий:
- CRUD — операции создания, чтения, обновления и удаления данных.
Social preview (рекомендуется): OG title: CRUD API на Node.js и PostgreSQL OG description: Пошаговое руководство по созданию простого REST CRUD API с Express и PostgreSQL.
Short announcement (для рассылки): Создано простое и понятное руководство по созданию CRUD API на Node.js с PostgreSQL. Шаги включают настройку сервера, подключение pg, реализацию CRUD-функций, маршрутов и рекомендации по безопасности и тестированию.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone