В этой статье мы продолжаем рассказывать о процессе создания интернет магазина. В первой части вы узнали, как создать дизайн сайта в Figma, а сегодня мы переходим к этапу создания RestAPI для нашего интернет-магазина. Для его создания мы используем Node.js, MongoDB и Mongoose для взаимодействия с ней. Перед началом написания Backend’а давайте изучим теорию про RestAPI и Mongoose.
Ознакомиться с полным кодом из данной статьи можно на GitHub.
Миграция в то самое облако
безопасно и с гарантией результата.
Предоставим грант до 1 000 000 ₽ на облачную
инфраструктуру и возьмем на себя весь процесс.
RestAPI
REST API (Representational State Transfer Application Programming Interface) — это архитектура построения веб-сервисов, основанная на принципах REST (представления состояния передачи), позволяющая клиентам взаимодействовать с сервером, запрашивая ресурсы (например, данные или операции над ними) посредством стандартных HTTP-запросов.
Ключевые принципы REST API
-
Использование HTTP-методов: Каждый метод имеет свое предназначение и соответствует конкретной операции CRUD (Create, Read, Update, Delete):
-
GET — получение ресурса (чтение)
-
POST — создание нового ресурса
-
PUT/PATCH — обновление существующего ресурса
-
DELETE — удаление ресурса
-
Адресация ресурсов: Ресурсы обозначаются уникальными URI (Uniform Resource Identifiers), которые однозначно определяют местоположение ресурса в пространстве имен сервера.
-
Без состояний (Stateless): Сервер не хранит состояние клиента между запросами. Вся необходимая информация передается клиентом вместе с каждым запросом.
-
Кэшируемость: Ответы от сервера могут кэшироваться, что улучшает производительность и снижает нагрузку на сервер.
-
Используемые форматы обмена данными: Чаще всего применяются JSON или XML, хотя возможны и другие форматы (например, YAML).
-
Обработка ошибок: Стандартизированные коды HTTP позволяют передавать клиенту статус успешности/неуспешности операций и причины возникновения ошибок.
Преимущества REST API
- Простота разработки и поддержки благодаря использованию общепринятых стандартов (HTTP);
- Гибкость и возможность интеграции с различными платформами и языками программирования;
- Масштабируемость и отказоустойчивость за счет архитектуры stateless;
- Возможность легкой интеграции с фронтендом (JavaScript, мобильные приложения и др.).
Таким образом, REST API — стандартизованный способ организации удаленного взаимодействия клиент-серверных приложений, который предоставляет четкую структуру и правила взаимодействия, удобные для разработчиков и пользователей сервисов.
Mongoose
Mongoose — это библиотека для Node.js, представляющая собой объектно-документный маппер (ODM) для базы данных MongoDB. Она служит мостом между вашей моделью данных и хранилищем, облегчает взаимодействие с базой данных и улучшает качество вашего кода.
Зачем нужен Mongoose
-
Моделирование данных: Вы можете определить схему ваших документов, включая типы данных, требования и ограничения. Например, вы определяете поля и проверяете их правильность перед записью в базу.
-
Валидаторы и проверка данных: Mongoose позволяет задать правила проверки данных, такие как обязательность полей, допустимые диапазоны значений, регулярные выражения и многое другое.
-
Средства сериализации: Когда вы извлекаете документ из базы данных, Mongoose преобразует его в удобный объект JavaScript, который можно использовать в вашем приложении.
-
Автоматическое управление версиями и история изменений: Через специальные плагины и расширения Mongoose поддерживает ведение истории изменений записей.
-
Поддержка сложных структур данных: В отличие от баз данных SQL, MongoDB позволяет сохранять вложенные структуры данных прямо внутри документов. Mongoose помогает удобно манипулировать такими объектами.
-
Ограничения целостности данных: Поддерживает отношения «один ко многим» и «многие ко многим», обеспечивая согласованность данных и связывая документы друг с другом.
-
Хуки и middleware: Можно добавить промежуточные обработчики (middleware), выполняющиеся до или после определенных действий (создание, чтение, обновление, удаление), что облегчает реализацию бизнес-логики.
-
Синхронизация и асинхронность: Mongoose работает синхронно с использованием обратных вызовов или асинхронно с промисами или
async/await.
При создании модели в Mongoose можно указывать ряд различных параметров и конфигураций, которые влияют на поведение и свойства самой модели и ее экземпляров. Эти настройки помогают точно настроить взаимодействие с базой данных, обеспечивают дополнительную безопасность и контроль над качеством данных.
Вот основные параметры и опции, доступные при создании модели.
Типы данных
Типы данных являются основой любой схемы Mongoose. Они указывают, какого типа должно быть значение каждого поля в документе. Примеры базовых типов:
- String: Используется для хранения строковых значений. Например, имена пользователей, адреса электронной почты и т.п.
- Number: Для численных значений — целых чисел (Integer) и вещественных (Float).
- Boolean: Логический тип данных, принимающий значения true или false.
- Date: Хранит временные метки (объекты типа JavaScript Date). Часто используется для полей вроде даты регистрации, последнего обновления записи и т.п.
- Buffer: Применяется для бинарных данных, например изображений или файлов.
- ObjectId: Уникальные идентификаторы объектов, автоматически генерируемые MongoDB. Обычно используются для связи между коллекциями (foreign keys).
- Mixed: Позволяет хранить данные любого типа. Полезно, если структура данных заранее неизвестна или гибкая.
- Array: Массив элементов одного или разных типов.
- Map: Использует объекты Map для динамических ключей и значений.
Требования к данным (Validation)
Можно задать дополнительные условия, которым должны соответствовать поля при сохранении в базу данных. Наиболее распространенные варианты:
required: обязательное наличие значения.unique: уникальное значение среди всех документов данной коллекции.default: значение по умолчанию.min/max: минимальное/максимальное число символов, длина массива и т.д.enum: перечисляемое значение, ограничивающее возможные варианты.match: регулярное выражение для соответствия определенному шаблону.validate: произвольная функция для дополнительной проверки данных.
Опции виртуальных полей (Virtuals)
Виртуальные поля позволяют вычислять значения на лету, основываясь на реальных полях документа. Они полезны для вывода расширенной информации, доступной только при чтении документа.
Пример виртуального поля:
Теперь каждый раз, когда документ извлекается из базы данных, доступ к полю fullName вернет полное имя пользователя.
Средства контроля и оптимизации производительности
Эти параметры задают внутренние характеристики поведения модели:
collection: имя коллекции, куда будут помещаться документы. Если не задано, оно формируется автоматически на основе имени модели.strict: режим строгого соблюдения схемы (по умолчанию включено). Запрещает сохранение полей, отсутствующих в схеме.autoIndex: автоматическая индексация полей, указанных в схеме.timestamps: автоматически добавляет поля createdAt и updatedAt, обновляемые при изменении документа.
Хуки (Middleware)
Вы можете назначать функции, которые будут выполняться до или после определенных операций (save, update, delete и т.д.). Это полезно для предварительной подготовки данных, отправки уведомлений, ведения логов и многого другого.
Связанные модели (References)
Mongoose позволяет устанавливать связи между моделями (документами), используя специальный тип данных ObjectId. Эта связь аналогична внешним ключам в реляционных СУБД.
Пример связанной модели:
Здесь author ссылается на коллекцию Users.
Используя механизм population, можно получать связанные документы:
Создание модели в Mongoose включает настройку множества аспектов, начиная от обязательных правил и ограничений, заканчивая поддержкой отношений между документами и интеграцией вспомогательных функций. Всё это позволяет строить высокопроизводительные и надежные решения, ориентированные на MongoDB.
Подготовка рабочего пространства
Подготовим наше пространство для работы с Node.js и cron-js.
Установка Node.js и npm
Чтобы запустить локальную разработку, нужно установить актуальную версию Node.js (рекомендуем v22.14.0 LTS). Вместе с Node.js установится npm — стандартный менеджер пакетов JavaScript.
Перейдите на официальный сайт nodejs.org и скачайте установщик.
Запустите его и следуйте инструкциям.
Настройка директории
-
Создайте новый каталог для проекта и сразу же перейдите в него:
-
Инициализируйте проект:
-
Установите необходимые зависимости:
Рассмотрим, для чего нам необходимы зависимости:
- Express: веб-фреймворк для серверной части приложений Node.js.
- Mongoose: библиотека для взаимодействия с базой данных MongoDB, упрощающая работу с моделями данных.
- Nodemailer: модуль для отправки электронных писем с сервера.
- Dotenv: утилита для загрузки переменных окружения из файла .env.
- Body-Parser: middleware для парсинга тела HTTP-запросов.
- Axios: библиотека для отправки HTTP запросов.
- Nodemon: инструмент разработчика, предназначенный для автоматического перезапуска узла (Node.js-приложения), когда обнаруживаются изменения в файлах проекта. Устанавливаем его отдельно, так как он нужен нам только в процессе разработки.
Настройка package.json
Изменим наш package.json для того, чтобы работал nodemon и зависимости были модулями. Обновленный файл package.json:
База данных
Для удобства работы в проекте сделаем следующую организацию файлов:

- Папка
controllerдля всех наших контроллеров - Папка
functionдля дополнительных функций, которые мы будем использовать - Папка
modelдля моделей MongoDB - Папка
node_modules— стандартная папка при установке зависимостей - Папка
routesдля назначения адресов - Файл
.envс настройками проекта - Файл
index.js— главный файл проекта - Файлы
package-lock.jsonиpackage.jsonс данными о проекте
Создание модели пользователя
Для создания конструкции документов необходимо создать файл модели. Начнем с модели пользователя. Переходим в папку model и создаем файл userModel.js.
Если представить в виде таблицы, то наша схема выглядит следующим образом:
|
firstName Текстовое Обязательное |
lastName Текстовое Обязательное |
Текстовое Уникальное |
phone Числовое Уникальное |
address Текстовое |
region Текстовое |
city Текстовое |
postCode Текстовое |
Создание модели товаров
Создайте файл productModel.js и добавьте следующий код:
Создание модели заказов
Создайте файл orderModel.js и добавьте следующий код:
В данном файле также присутствует функция createdAt. Мы используем ее, чтобы получить дату в определенном виде для дальнейшей работы.
Создание контроллеров
Контроллер — это компонент архитектуры MVC (Model–View–Controller), ответственный за обработку входящих HTTP-запросов, выполнение необходимой бизнес-логики и отправку результата обратно клиенту. Контроллеры служат посредниками между входящими запросами и логикой приложения, эффективно отделяя уровень представления (view) от уровня данных (model).
Создание контроллера пользователя
В начале файла каждого контроллера у нас находится импорт модели, с которой мы работаем. Для пользователя это модель userModel.js, пишем в файл userController.js:
Нам необходимо создать четыре операции: Create, Read, Update, Delete (CRUD).
Создание нового пользователя
Для создания пользователя нам нужно реализовать следующую логику:
- Получаем JSON.
- Проверяем, что такого пользователя не существует.
- Если пользователь уникален, добавляем запись.
Для проверки уникальности мы будем использовать почту, так как остальные данные могут быть не уникальными.
Перейдите в папку controller, создайте файл userController.js и напишите следующий код:
Для определения уникальности пользователя мы получаем из JSON почту и, при помощи метода findOne с переданным значением почты, получаем либо пустое значение, либо запись пользователя.
Если пользователь уже существует, контроллер вернет статус 400 и напишет «User already exists» (пользователь уже существует). В случае появления каких либо ошибок контроллер вернёт статус 500 и напишет «Internal Server Error» (внутренняя ошибка сервера).
Получение всех пользователей
Здесь выполняется проверка на наличие хоть каких-то записей пользователей. Если они отсутствуют, то контроллер отправит статус 404 и сообщение «User not Found» (пользователь не найден). Как и ранее, добавлен обработчик ошибок сервера.
Обновление данных пользователя
Здесь мы получаем из запроса id пользователя и ищем его в базе. Если он отсутствует, то контроллер отправит статус 404 и сообщение «User not Found» (пользователь не найден). Если пользователь найден, то при помощи метода findByIdAndUpdate происходит обновление данных в записи. Как и ранее, добавлен обработчик ошибок сервера.
Удаление пользователя
Перед созданием функции для удаления стоит отметить что есть два варианта удаления записей из базы данных: soft-delete (мягкое удаление) и hard-delete (жесткое удаление).
hard-delete — это обычное удаление записи. А soft-delete — это техника удаления данных, при которой запись физически не уничтожается из базы данных, а помечается специальным флагом, означающим, что она больше не должна отображаться пользователям или считаться активной. Такой подход широко применяется там, где важно сохранить историю изменения данных или предотвратить потерю важной информации.
Обычно при мягком удалении добавляется специальное поле (как правило, называется deletedAt, isDeleted, archived и пр.), которое устанавливается в boolean-значение (или в дату удаления), когда выполняется операция удаления. Затем этот флаг учитывается при выборке данных, фильтруя удаленные элементы из результатов.
В данном примере у нас нет необходимости хранить старых пользователей, поэтому мы используем hard-delete.
Как и ранее, у нас добавлена проверка на наличие пользователя, обработчик внутренних ошибок и вывод после удаления пользователя.
Создание контроллера для продуктов
По аналогии с контроллером пользователя создадим контроллер для заказов. Создайте файл productController.js и добавьте следующий код:
Создание контроллера заказов
Создайте файл orderController.js и добавьте следующий код:
Обратим внимание на fetch2 и fetch3. В них мы получаем активные и архивные заказы — для этого анализируем JSON на наличие в status записи «Заказ вручен! Спасибо за покупку».
Создание роутеров
Для того чтобы наши контроллеры могли работать, необходимо создать так называемый endpoint.
Роутеры — это функции из Express, поэтому в начале файла мы подключаем Express, а также контроллер для взаимодействия с его функциями, написанными ранее.
Роутер для пользователя
Перейдите в папку routes, создайте файл userRoute.js и добавьте следующий код:
Здесь мы импортируем express и наши функции из контроллера пользователя, объявляем переменную route и прописываем адреса. Обратите внимание, что для каждой функции — разный тип запроса (GET, POST, PUT, DELETE). Теперь для взаимодействия с контроллерами у нас есть конечные точки:
/getallusers— получение всех пользователей/create— создание нового пользователя/update/:id— обновление пользователя, в качестве аргумента мы принимаемid/delete/:id— удаление пользователя, в качестве аргумента принимаемid
Роутер для продуктов
Создайте файл productRoute.js и добавьте следующий код:
Роутер для заказов
Создайте файл orderRoute.js и вставьте код:
Создание главного файла
После создания всех указанных файлов мы можем переходить к созданию главного файла нашей backend-части.
Создайте в корневой директории проекта файл index.js и импортируйте все необходимые модули:
Подключение к базе данных
Для начала нам необходимо получить нашу базу данных, для удобства будем использовать облачные возможности, а именно DBaaS. Перейдем в панель управления Timeweb Cloud и откроем раздел «Базы данных».
Нажимаем на кнопку «Создать» в правом верхнем углу и выбираем MongoDB с конфигурацией: CPU 1 x 3.3ГГц, RAM 2 ГБ, NVMe 20 ГБ, 1 IPv4 стоимостью 600 ₽ в месяц.

Ждем установки базы данных и копируем данные для подключения из панели управления:

Настройка .env
Для безопасности необходимо использовать файл .env. Создаем файл и пишем в него следующее:
<user>— логин пользователя<password>— пароль пользователя<ip>— публичный IP<db>— название базы данных
Также добавим запись с портом, на котором будем запускать сервер:
Настройка подключения
Возвращаемся в файл index.js и пишем следующий код:
Здесь:
-
Используем
dotenv.config()для определения модуляdotenvи устанавливаем константы с данными изenv-файла. -
Подключаемся к базе данных при помощи
mongoose.connect(), где в качестве аргумента передаем ссылку для подключения. В случае удачного подключения выводим готовность базы данных и запускаем сервер на Express, а в случае появления ошибок выводим их в консоль и не даем запуститься серверу.
Подключение роутеров
Теперь для того, чтобы наши роутеры стали работать, подключим их к главному файлу нашего проекта. После подключения всех модулей добавим следующее:
Далее объявим переменную для работы Express и активируем body-parser:
И в самый конец файла добавим начальные пути до наших роутов:
В итоге мы получаем следующее содержимое:
Теперь, чтобы получить всех пользователей, мы используем путь /api/user/getallusers.
Запуск
Запускаем наше приложение при помощи команды:
Если всё сделано правильно, мы получим вывод:

Тестирование
Для проверки нашего RestAPI я рекомендую использовать Postman. Переходим на официальный сайт и скачиваем версию для своей ОС.

Открываем Postman и видим следующий интерфейс:

Нажимаем на плюс сверху экрана, и перед нами открывается страница, где мы можем отправлять запросы:

Пользователь
Напишем запрос для получения всех пользователей:
Отправим и получим:

Сервер нам пишет? что пользователей не найдено. Верно, нам необходимо их создать. Пишем адрес для создания пользователя:
Мы определили этот запрос как POST, поэтому в контекстном меню выбираем POST:

Далее нам необходимо передать данные. Для этого переходим в body, выбираем тип raw и вставляем данные:

Теперь проверим созданных пользователей и получим:

Для обновления пользователя используем PUT, пишем адрес:
Вместо {:id} вставьте id пользователя.
В body вставим то же самое, что и для создания, но изменим фамилию с «Фамилия» на «Фамилия update». Отправим и получим:

И проверим адрес для удаления, используя тип DELETE и адрес:
Вместо {:id} вставьте id пользователя.
Отправляем и получаем:

Теперь по аналогии проверим все остальные пути.
Продукты
Получение всех продуктов (GET):
Получение продукта по id (GET):
Создание (POST):
В body указываем следующий JSON:
Обратим внимание на спецификации (цвет, размер), они сделаны так, что у каждого цвета футболки указан размер и количество товара с именно этими спецификациями
Обновление (PUT):
Удаление (DELETE):
Заказы
Получение всех заказов (GET):
Получение активных заказов (GET):
Получение архивных заказов (GET):
Создание (POST):
И в body укажем следующее:
Обновление (PUT):
Удаление (DELETE):
Дополнительные функции
После заказа на указанную почту необходимо отправить уведомление о том, что заказ создан. Так как у нас магазин, то мы отправим чек. Для этого воспользуемся простым HTML-шаблоном:

Так как заказ должен отправляться на электронную почту, мы воспользуемся зависимостью nodemailer и почтой от Timeweb Cloud.
Для почты нам нужен домен. Переходим в панель управления, в меню выбираем «Домены и SSL» и нажимаем кнопку «Купить домен».
Сейчас необходимо определиться с тем, как наш сайт будет находится в сети, поэтому к этому этапу необходимо отнестись серьезно.
После покупки домена убедитесь, что он появился у вас в панели управления и перейдите на страницу «Почта» и выберите «Создать ящик».

После создания почты вернитесь в IDE, и запишите в .env-файл данные от почты:
Далее перейдите в папку function и создайте файл createCheque.js:
Импортируйте модули:
Настройте подключение .env-файла:
Настройте nodemailer:
Далее создадим функцию для получения стоимости товара:
Обратите внимание, что в этой строке:
— вместо <server_address> надо написать адрес нашего сервера, для локального тестирования это http://localhost:5001.
Функция для обработки данных товара:
Функция для создания HTML-кода:
Код для отправки письма:
Обратите внимание, что в строке:
— вместо <username> необходимо написать ваш почтовый адрес, с которого будет производиться отправка писем.
И в завершение для получения данных из JSON для чека обернем все созданные функции в функцию:
Данную функцию мы будем вызывать из контроллера, поэтому добавили к ней экспорт.
Переходим в файл controller/orderController.js и добавляем в начало после импорта модели импорт ранее созданного файла:
Добавим в create строчку кода, которая будет вызывать отправку письма. Обновленный код:
Полный код файла createCheque.js
Сохраните и проверьте что сервер запущен.
Перейдите в Postman и отправьте запрос на создание заказа. Проверьте, что в поле email находится действительный адрес почты. Если заказ успешно создан, переходим в почту и видим письмо:

Заключение
В этой статье мы познакомились с MongoDB, научились создавать базы данных и взаимодействовать с ними из Node.js при помощи Mongoose и Express, а также создали RestAPI для нашего интернет магазина.
Дальше можно приступать к разработке фронтенда.
