- Что мы соберем
- Что нужно заранее
- Схема проекта
- Создаем проект
- Готовим таблицу
- Настройки подключения
- Подключение к MySQL
- Первый server.js
- Получаем список заявок
- Добавляем заявку
- Общая обработка ошибок
- Полный server.js
- Проверка через curl
- Частые ошибки
- Access denied for user
- Unknown database
- ECONNREFUSED
- Данные вставляются, но русский текст ломается
- SQL собирается через шаблонную строку
- Что улучшить дальше
- Ответы на эти вопросы могут быть для вас полезными
- Можно ли использовать MySQL с Node.js?
- Что лучше для первого проекта: MySQL или MongoDB?
- Почему нельзя хранить пароль базы в коде?
- Зачем нужен pool подключений?
- Это уже полноценный backend?
- Что почитать дальше по Node.js
Что мы соберем
В этом уроке сделаем маленький REST API на Node.js, Express и MySQL. Сценарий приземленный: у нас есть форма заявки на сайте, и мы хотим сохранять имя, email и статус в таблицу
Это хороший первый проект, потому что в нем сразу видны важные backend-кусочки:
- HTTP-сервер;
- JSON-запросы;
- подключение к базе;
- SQL-таблица;
- параметризованные запросы;
- простая проверка входных данных.
Что нужно заранее
Нужны:
- установленный Node.js LTS;
- npm;
- MySQL-сервер локально или в Docker;
- базовое понимание Express;
- любой клиент для проверки запросов: curl, Postman, Insomnia или REST Client в VS Code.
Если MySQL еще не стоит, можно сначала прочитать SQL-блок и вернуться сюда позже. Смысл урока не в установке MySQL, а в связке Node.js + база
Схема проекта
node-mysql-api/
package.json
server.js
db.js
.env
В server.js будет Express. В db.js — подключение к MySQL. В .env — настройки подключения
Создаем проект
mkdir node-mysql-api
cd node-mysql-api
npm init -y
npm install express mysql2 dotenv
Пакеты:
express— сервер и маршруты;mysql2— драйвер для MySQL;dotenv— чтение переменных окружения из.env.
Готовим таблицу
Создадим базу и таблицу:
CREATE DATABASE solo_lessons;
USE solo_lessons;
CREATE TABLE leads (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(120) NOT NULL,
email VARCHAR(180) NOT NULL,
status VARCHAR(40) NOT NULL DEFAULT 'new',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Для первого урока этого достаточно. В реальном проекте можно добавить индексы, уникальность email, источник заявки, телефон, комментарий, UTM-метки
Настройки подключения
Создайте .env:
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=solo_lessons
DB_PORT=3306
PORT=3000
Пароль не пишите прямо в server.js. Сегодня это учебный проект, завтра код случайно окажется в репозитории
Подключение к MySQL
Создайте db.js:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
port: Number(process.env.DB_PORT || 3306),
waitForConnections: true,
connectionLimit: 10
});
module.exports = pool;
Мы используем pool, а не одно вечное соединение. Для API это нормальнее: сервер может обрабатывать несколько запросов, а pool управляет подключениями
Первый server.js
require('dotenv').config();
const express = require('express');
const pool = require('./db');
const app = express();
const port = Number(process.env.PORT || 3000);
app.use(express.json());
app.get('/api/status', async (req, res) => {
const [rows] = await pool.query('SELECT 1 AS ok');
res.json({
ok: true,
database: rows[0].ok === 1
});
});
app.listen(port, () => {
console.log(`API запущен: http://localhost:${port}`);
});
Запуск:
node server.js
Проверка:
curl http://localhost:3000/api/status
Если база подключена, получите JSON со статусом
Получаем список заявок
Добавим маршрут:
app.get('/api/leads', async (req, res, next) => {
try {
const [rows] = await pool.query(
'SELECT id, name, email, status, created_at FROM leads ORDER BY id DESC'
);
res.json({
ok: true,
leads: rows
});
} catch (error) {
next(error);
}
});
Пока таблица пустая, это нормально. API должен вернуть пустой массив, а не падать
Добавляем заявку
Теперь POST /api/leads:
app.post('/api/leads', async (req, res, next) => {
try {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
ok: false,
message: 'Нужны name и email'
});
}
const [result] = await pool.execute(
'INSERT INTO leads (name, email, status) VALUES (?, ?, ?)',
[name, email, 'new']
);
res.status(201).json({
ok: true,
lead: {
id: result.insertId,
name,
email,
status: 'new'
}
});
} catch (error) {
next(error);
}
});
Обратите внимание на ? в SQL-запросе. Это параметризованный запрос. Мы не склеиваем SQL строкой из пользовательского ввода, потому что это прямой путь к SQL-инъекциям
Общая обработка ошибок
В конце server.js, перед app.listen, добавьте:
app.use((error, req, res, next) => {
console.error(error);
res.status(500).json({
ok: false,
message: 'Внутренняя ошибка сервера'
});
});
Для учебного проекта достаточно. В продакшене ошибки логируют аккуратнее и не показывают пользователю внутренние детали
Полный server.js
require('dotenv').config();
const express = require('express');
const pool = require('./db');
const app = express();
const port = Number(process.env.PORT || 3000);
app.use(express.json());
app.get('/api/status', async (req, res, next) => {
try {
const [rows] = await pool.query('SELECT 1 AS ok');
res.json({
ok: true,
database: rows[0].ok === 1
});
} catch (error) {
next(error);
}
});
app.get('/api/leads', async (req, res, next) => {
try {
const [rows] = await pool.query(
'SELECT id, name, email, status, created_at FROM leads ORDER BY id DESC'
);
res.json({
ok: true,
leads: rows
});
} catch (error) {
next(error);
}
});
app.post('/api/leads', async (req, res, next) => {
try {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
ok: false,
message: 'Нужны name и email'
});
}
const [result] = await pool.execute(
'INSERT INTO leads (name, email, status) VALUES (?, ?, ?)',
[name, email, 'new']
);
res.status(201).json({
ok: true,
lead: {
id: result.insertId,
name,
email,
status: 'new'
}
});
} catch (error) {
next(error);
}
});
app.use((error, req, res, next) => {
console.error(error);
res.status(500).json({
ok: false,
message: 'Внутренняя ошибка сервера'
});
});
app.listen(port, () => {
console.log(`API запущен: http://localhost:${port}`);
});
Проверка через curl
Добавить заявку:
curl -X POST http://localhost:3000/api/leads \
-H "Content-Type: application/json" \
-d '{"name":"Анна","email":"anna@example.com"}'
Получить список:
curl http://localhost:3000/api/leads
Если заявка появилась в списке, связка Node.js + MySQL работает
Частые ошибки
Access denied for user
Неверный пользователь или пароль в .env. Проверьте DB_USER и DB_PASSWORD
Unknown database
База не создана или указано другое имя. Выполните CREATE DATABASE или поправьте DB_NAME
ECONNREFUSED
MySQL-сервер не запущен или порт другой. Проверьте службу MySQL и DB_PORT
Данные вставляются, но русский текст ломается
Проверьте кодировку базы и таблицы. Обычно для новых проектов стоит использовать utf8mb4
SQL собирается через шаблонную строку
Не делайте так:
`SELECT * FROM leads WHERE email = '${email}'`
Используйте параметры:
pool.execute('SELECT * FROM leads WHERE email = ?', [email]);
Что улучшить дальше
После первого результата можно добавить:
- валидацию email;
PUT /api/leads/:idдля изменения статуса;DELETE /api/leads/:id;- пагинацию;
- логирование запросов;
- Docker Compose для MySQL;
- миграции вместо ручного SQL.
Ответы на эти вопросы могут быть для вас полезными
Можно ли использовать MySQL с Node.js?
Да. Для этого нужен драйвер, например mysql2, и обычные SQL-запросы из Node.js-кода
Что лучше для первого проекта: MySQL или MongoDB?
Если данные табличные и важны связи, MySQL понятен. Если данные похожи на гибкие документы, можно взять MongoDB. Для обучения полезны оба подхода
Почему нельзя хранить пароль базы в коде?
Код легко попадает в Git, архив, переписку или скриншот. Настройки подключения лучше держать в переменных окружения
Зачем нужен pool подключений?
API обрабатывает много запросов. Pool помогает переиспользовать подключения к базе и не открывать новое соединение на каждый запрос вручную
Это уже полноценный backend?
Это учебный скелет backend. Для продакшена нужны валидация, авторизация, логирование, защита, миграции, мониторинг и нормальный деплой
Что почитать дальше по Node.js
Если вы собираете тему по шагам, рядом лучше открыть:
- Первый сервер на Express — разобрать основу маршрутов Express перед базой данных.
- Node.js + MongoDB: первая коллекция из приложения — сравнить SQL-подход с документной базой.
- Хостинг для Node.js сайта: что проверять перед выбором — подготовить API к размещению на сервере.
- Node.js LTS: какую версию ставить новичку — выбрать версию Node.js, которую поддерживает драйвер и хостинг.



