Современные веб-приложения почти всегда состоят из двух частей: клиентской, которую видит пользователь, и серверной, которая управляет данными и бизнес-логикой. Связующим звеном между ними выступает именно REST API. Платформа Node.js и построенный поверх неё фреймворк Express сегодня являются одним из самых популярных решений для backend-разработки благодаря своей простоте, высокой производительности и огромной экосистеме готовых пакетов. В этой статье мы шаг за шагом разберём, как построить полноценный REST API с нуля, изучим маршруты и промежуточные обработчики, операции CRUD, обработку ошибок, валидацию и аутентификацию на практических примерах кода.
Что такое Express и зачем он нужен
Express — это минималистичный и гибкий веб-фреймворк для Node.js, который значительно упрощает процесс приёма HTTP-запросов и формирования ответов. Сам по себе Node.js тоже позволяет создать веб-сервер, однако в этом случае вам пришлось бы вручную разбирать каждый запрос, сравнивать URL-адреса и настраивать заголовки ответа. Express скрывает эту сложность и предоставляет удобные инструменты в виде маршрутов, промежуточных обработчиков и вспомогательных методов. Именно поэтому тысячи компаний и стартапов строят свои API на основе Express, ведь он позволяет добиваться большего результата меньшим количеством кода.
Настройка проекта и первый сервер
Чтобы начать работу, на вашем компьютере должен быть установлен Node.js. Затем мы создаём новую папку проекта, инициализируем его с помощью менеджера пакетов npm и устанавливаем библиотеку Express. Следующие команды подготавливают базовую структуру проекта и создают основу для написания первого работающего сервера.
mkdir my-api && cd my-api
npm init -y
npm install express
Теперь в корне проекта создаём файл index.js и пишем простейший сервер. Этот сервер отвечает лишь на один адрес, но именно он служит фундаментом всего нашего будущего API. Обратите внимание, что сервер прослушивает определённый порт и при запуске выводит сообщение в консоль, чтобы мы понимали, что всё работает корректно.
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.json({ message: 'API работает' });
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Сервер запущен на порту ${PORT}`);
});
Маршруты и эндпоинты CRUD
Основная идея REST API заключается в работе с ресурсами, например пользователями или статьями. Над каждым ресурсом выполняются четыре основные операции: создание, чтение, обновление и удаление, то есть CRUD. Эти операции выражаются соответственно через HTTP-методы POST, GET, PUT и DELETE. В приведённом ниже примере мы рассмотрим полный набор CRUD-эндпоинтов, работающих с временным хранилищем данных в виде обычного массива.
let users = [{ id: 1, name: 'Иван' }];
app.get('/users', (req, res) => {
res.json(users);
});
app.get('/users/:id', (req, res) => {
const user = users.find(u => u.id === Number(req.params.id));
if (!user) return res.status(404).json({ error: 'Не найдено' });
res.json(user);
});
app.post('/users', (req, res) => {
const user = { id: Date.now(), name: req.body.name };
users.push(user);
res.status(201).json(user);
});
app.delete('/users/:id', (req, res) => {
users = users.filter(u => u.id !== Number(req.params.id));
res.status(204).end();
});
В этом коде через req.params мы читаем динамические части URL, а через req.body — данные JSON из тела запроса. При формировании ответа крайне важно правильно выбирать коды состояния HTTP: при создании нового ресурса возвращается 201, при успешном удалении — 204, а если ресурс не найден — 404. Эти коды чётко объясняют клиентам API, что именно произошло с их запросом.
Концепция промежуточных обработчиков
Промежуточные обработчики, или middleware, — это функции, которые выполняются в промежутке между поступлением запроса на сервер и отправкой ответа. С их помощью вы можете централизованно решать такие задачи, как логирование запросов, проверка аутентификации, предварительная обработка данных или перехват ошибок. Использованный выше express.json() на самом деле тоже является middleware, который автоматически преобразует входящее JSON-тело в объект. Написать собственный обработчик тоже совсем несложно.
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`);
next();
}
app.use(logger);
Обратите внимание, что в конце каждого middleware необходимо вызывать функцию next(), иначе запрос не перейдёт к следующему этапу и просто зависнет. Этот механизм делает Express чрезвычайно мощным, поскольку он позволяет разбивать сложную логику на небольшие переиспользуемые фрагменты.
Обработка ошибок и валидация
При создании надёжного API одним из самых недооценённых, но при этом важнейших аспектов является корректная обработка ошибок. Пользователь может отправить неверные данные, запросить несуществующий ресурс или столкнуться с непредвиденной проблемой на сервере. В таких ситуациях API обязан вернуть понятное сообщение об ошибке и правильный код состояния. В Express специальный обработчик ошибок принимает четыре аргумента и размещается после всех маршрутов.
app.post('/users', (req, res, next) => {
if (!req.body.name) {
return res.status(400).json({ error: 'Имя обязательно' });
}
next();
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Ошибка сервера' });
});
Валидация, то есть проверка входящих данных, также чрезвычайно важна с точки зрения безопасности. На практике многие разработчики вместо ручного написания такой логики используют специализированные библиотеки, например Joi или express-validator, что помогает сохранять код чистым и надёжным.
База данных и аутентификация
В реальных проектах данные хранятся не в массиве, а в базе данных. Node.js легко подключается к MongoDB через Mongoose или к PostgreSQL через такие инструменты, как Prisma. Соединение обычно устанавливается один раз при запуске приложения, а затем внутри маршрутов через это соединение выполняются запросы. Аутентификация чаще всего реализуется с помощью технологии JWT, то есть JSON Web Token: при входе пользователю выдаётся специальный токен, и в последующих запросах именно по этому токену подтверждается его личность.
const jwt = require('jsonwebtoken');
function auth(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'Нет токена' });
try {
req.user = jwt.verify(token, process.env.SECRET);
next();
} catch {
res.status(403).json({ error: 'Неверный токен' });
}
}
Лучшие практики
Чтобы создать API профессионального уровня, стоит придерживаться нескольких важных правил. Во-первых, разделяйте код на логические слои: маршруты, бизнес-логика и работа с данными в отдельных файлах значительно упрощают сопровождение проекта по мере его роста. Во-вторых, никогда не записывайте секретные данные, такие как пароли от базы данных и ключи токенов, напрямую в код — храните их в переменных окружения. В-третьих, внедряйте версионирование API и постоянно применяйте меры безопасности, например ограничение частоты запросов. Соблюдая эти практики, вы получите не просто работающий, а действительно надёжный API, способный выдержать настоящую нагрузку.