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

Использование Mongoose с Express для работы с MongoDB

6 min read Бэкенд Обновлено 22 Dec 2025
Mongoose + Express: руководство по работе с MongoDB
Mongoose + Express: руководство по работе с MongoDB

Ноутбук с двумя наложенными логотипами: слева логотип MongoDB, справа логотип JavaScript

В управлении данными MongoDB есть и сила, и ответственность. MongoDB — документная, «безсхемная» база данных: это даёт гибкость, но может привести к непредсказуемым структурам данных. Mongoose вводит схему и поведенческие контракты (валидации, хуки, методы), которые помогают сохранить целостность данных и упростить работу с коллекциями в приложении на Express.

В этой статье вы получите практическое руководство: от установки до продвинутых приёмов валидации, миграций и отладки, а также чек-листы для ролей в команде.

Что такое Mongoose — определение в одну строку

Mongoose — это ODM (Object Data Modeling) для Node.js, которое добавляет схему, валидацию и удобную модель работы с коллекциями MongoDB.

Быстрая проверка требований

  • Node.js v12+ (рекомендуется последняя LTS).
  • Локальный или удалённый MongoDB (URI доступа).
  • Проект на Express или другой Node-сервер.

Установка и подключение

Установите Mongoose как зависимость проекта:

npm install mongoose

Установленный пакет нужно подключить и установить соединение с базой. Простой пример подключения к локальной базе:

// index.js
const mongoose = require("mongoose")

mongoose.connect("mongodb://127.0.0.1:27017/example", () =>
  console.log("Connected to database successfully")
);

Советы по подключению:

  • Для production используйте строку подключения с учётом аутентификации и TLS/SSL.
  • Включите повторные попытки подключения и управляйте таймаутами через опции mongoose.connect.
  • Храните URI в защищённом хранилище (переменные окружения, секреты).

Important: не оставляйте креденшалы в коде.

Определение схемы и модели

Схема описывает структуру документов, их типы и валидации. Модель — это класс, через который вы работаете с коллекцией.

Пример схемы для коллекции User:

const mongoose = require("mongoose");

const userSchema = mongoose.Schema({
  name: {
    type: String,
    required: [true, "Name is required"],
  },
  email: {
    type: String,
    required: true,
  },
  age: {
    type: Number,
    validate: {
      validator: function (value) {
        return value > 0;
      },
      message: () => "Please enter a valid age",
    },
  },
});

const User = mongoose.model("User", userSchema);

module.exports = User;

Коротко о полях:

  • name: строка, обязательна.
  • email: строка, обязательна (в реальном проекте добавьте проверку формата).
  • age: число с пользовательской валидацией (должно быть > 0), необязательное.

Параметры схемы, полезные для production:

  • timestamps: true — автоматически добавляет createdAt и updatedAt.
  • versionKey: false — отключает поле __v, если не нужно.
  • strict: ‘throw’ — выбрасывать ошибку при попытке записать неизвестные поля.

Пример с опциями:

const userSchema = mongoose.Schema({ /* поля */ }, { timestamps: true, versionKey: false });

CRUD: создание, чтение, обновление, удаление

Обратите внимание: импортируйте модель в модуле, где выполняете операции.

// router.js
const User = require("./userModel")

Создание документа — несколько подходов:

  1. Создать экземпляр и вызвать save():
let user = new User({ name, email, age });
user.save()
  .then(() => console.log("User created successfully"))
  .catch((error) => { /* обработка ошибок */ });
  1. Метод create() — сочетает создание и сохранение:
User.create({ name, email, age }, (err, data) => {
  if (err) throw new Error("Internal Server Error");
  console.log(`User created successfully: ${data}`);
});
  1. insertMany() — для пакетной вставки:
User.insertMany(
  [
    { name, email, age },
    { name_1, email_1, age_1 },
  ],
  (err, result) => {
    if (err) { /* обработка ошибок */ } else { /* отправка результата */ }
  }
);

Чтение документов:

  • find({}) — вернуть все документы.
  • find(query) — вернуть все подходящие документы.
  • findById(id) — найти по _id.
  • findOne(filter) — вернуть первый подходящий документ.

Примеры:

User.find({})
  .then((data) => console.log(data))
  .catch((err) => { /* обработка */ });

User.find({ age: { $gte: 18 } })
  .then((data) => console.log(data))
  .catch((error) => console.log(error));

User.findById(id, (error, result) => {
  if (result) console.log(result);
  if (error) console.error(error);
});

User.findOne({ email: "johnson@example.com" }).then((user) => {
  if (!user) { /* обработка */ }
  // отправка ответа
});

Обновление:

User.findByIdAndUpdate(id, req.body, (err, doc) => {
  if (doc) { /* отправить ответ */ }
  if (err) { /* обработка ошибки */ }
});

Рекомендуется использовать опцию { new: true } чтобы получить обновлённый документ, и { runValidators: true } чтобы применять валидации при обновлении.

Удаление:

User.findByIdAndDelete(id, (error, result) => {
  if (result) { /* обработка результата */ }
  if (error) { /* обработка ошибки */ }
});

Продвинутые возможности схем

Mongoose предлагает дополнительные механизмы, которые повышают выразительность моделей:

  • Hooks / Middleware: pre и post хуки (save, validate, remove, findOneAndUpdate и т.д.).
  • Virtuals: вычисляемые поля, которые не сохраняются в БД, но формируются при сериализации.
  • Statics и methods: статические методы модели и методы экземпляра.
  • Query helpers: расширяют цепочку запросов.
  • Population: связывает документы через ObjectId и подгружает данные связанной коллекции.
  • Индексы: schema.index() и model.collection.createIndex() для производительности.

Примеры хуков и виртуалов:

userSchema.pre('save', function(next) {
  // this — документ
  this.updatedAt = Date.now();
  next();
});

userSchema.virtual('isAdult').get(function() {
  return this.age >= 18;
});

userSchema.methods.fullInfo = function() {
  return `${this.name} <${this.email}>`;
};

Population пример:

// В схеме Post есть поле author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
Post.find().populate('author').then(posts => console.log(posts));

Транзакции и сессии

Для атомарных операций на нескольких документах и коллекциях используйте транзакции MongoDB (при работе с replica set или sharded cluster). Mongoose поддерживает транзакции через session:

const session = await mongoose.startSession();
try {
  session.startTransaction();
  await ModelA.create([{ /*...*/ }], { session });
  await ModelB.updateOne({ /*...*/ }, { /*...*/ }, { session });
  await session.commitTransaction();
} catch (err) {
  await session.abortTransaction();
} finally {
  session.endSession();
}

Используйте транзакции там, где требуется согласованность между несколькими коллекциями.

Обработка ошибок

Типичные источники ошибок:

  • Валидация (ValidationError).
  • Дублирование уникального поля (E11000 duplicate key error).
  • Ошибки подключения.
  • Некорректные ObjectId при findById.

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

  • Централизуйте обработку ошибок в middleware Express.
  • Ловите ValidationError и возвращайте понятный клиенту ответ со статусом 400.
  • Логируйте ошибки уровня сервера и сохраняйте трассировки для отладки.

Тестирование и локальные среды

  • Для модульных тестов используйте in-memory MongoDB (mongodb-memory-server) или sandbox-базу.
  • Для интеграционных тестов разворачивайте replica set, если тестируете транзакции.
  • Сбрасывайте данные между тестами (dropDatabase или транзакции для отката).

Пример с mongodb-memory-server:

const { MongoMemoryServer } = require('mongodb-memory-server');
const mongod = await MongoMemoryServer.create();
const uri = mongod.getUri();
await mongoose.connect(uri);

Производительность и индексация

  • Индексируйте поля для фильтров и сортировок.
  • Используйте projection (select) чтобы возвращать только нужные поля.
  • Пагинация через cursor/skip+limit или лучше — через range-пагинацию по индексу.
  • Избегайте частых запросов «N+1» — применяйте populate аккуратно.

Пример создания индекса:

userSchema.index({ email: 1 }, { unique: true });

Миграции и эволюция схемы

Поскольку MongoDB гибкая по структуре, изменения схемы требуют дисциплины:

  • Используйте миграционные утилиты (migrate-mongo, umzug, migrate).
  • Пишите миграции, которые безопасно обновляют документы по шагам.
  • Подумайте о versioning-полях в документах для отката/совместимости.

Мини-методология миграции:

  1. Добавьте новое поле с default/nullable вариантом.
  2. Обновите код для чтения и записи нового поля (поддержка обеих версий).
  3. Запустите миграцию в фоне, обновляющую документы пакетно.
  4. Через время используйте cleanup-миграцию и удалите legacy-ветки кода.

Альтернативы Mongoose

  • MongoDB Native Driver — ближе к базе, меньше абстракции, больше контроля.
  • Prisma — современный ORM с генерацией типов, но поддержка MongoDB есть с оговорками.
  • TypeORM — преимущественно для SQL, но имеет ограниченную поддержку MongoDB.

Когда Mongoose не подходит:

  • Нужна максимальная скорость и минимальная накладная — выбирайте native driver.
  • Вы хотите строгую типизацию на уровне TypeScript + генерацию миграций — рассмотрите Prisma.

Ментальные модели и эвристики

  • Схема — контракт между приложением и данными.
  • Меньше повторной логики в запросах — больше ответственности у моделей.
  • Валидируйте на двух уровнях: клиент + сервер (Mongoose).
  • Делайте миграции обратимыми и поэтапными.

Ролевые чек-листы

Разработчик:

  • Добавил схему и валидации.
  • Написал unit-тесты для валидации и методов модели.
  • Убедился, что операции используют индексы.

DevOps:

  • Настроил replica set для транзакций и резерва.
  • Настроил мониторинг подключений и латентности.
  • Обеспечил безопасное хранение URI и секретов.

QA:

  • Покрыл позитивные и негативные сценарии API.
  • Проверил работу с невалидными payload.
  • Протестировал миграции на копии данных.

Архитектор:

  • Решил, где нужна нормализация, а где — вложенные документы.
  • Утвердил стратегию ретеншн и архивирования данных.

Примеры приёма и тест-кейсы

Критерии приёмки для создания пользователя:

  • При валидном payload пользователь сохраняется и возвращается 201.
  • При отсутствии обязательного поля name возвращается 400 с сообщением.
  • При дублирующемся email возвращается 409 или 400 с пояснением.

Тест-кейс на пагинацию:

  • Запрос /users?page=2&limit=10 возвращает корректный набор пользователей и мета-информацию.

Шаблон SOP быстрой проверки инцидента

  1. Проверить доступность MongoDB (ping, подключение).
  2. Проверить логи приложения на ошибки подключения или таймауты.
  3. Если проблема в индексе — временно уменьшить нагрузку, восстановить индекс.
  4. Оповестить команду и откатить недавние миграции, если они связаны с инцидентом.

Короткий глоссарий

  • ODM: Object Data Modeling — слой между приложением и БД.
  • Schema: определение структуры документа.
  • Model: класс для операций с коллекцией.
  • Population: подгрузка связанных документов по ObjectId.

Короткое резюме

  • Mongoose даёт схему и удобный API поверх MongoDB, снижая вероятность ошибок в данных.
  • Используйте валидации, индексы и транзакции там, где нужно.
  • Тестируйте и планируйте миграции поэтапно.

Notes: если нужен пример схемы с TypeScript типами, миграционным скриптом или готовый playbook для деплоя с тревогами — могу подготовить отдельно.

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

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

Восстановление системного образа Windows
Резервное копирование

Восстановление системного образа Windows

Управление разделами и томами в Windows 10
Windows

Управление разделами и томами в Windows 10

UFW — настройка брандмауэра в Linux
Системное администрирование

UFW — настройка брандмауэра в Linux

Диаграммы в Google Docs: быстро и просто
Google Docs

Диаграммы в Google Docs: быстро и просто

Как импортировать плейлисты в Spotify
Музыка

Как импортировать плейлисты в Spotify

Как изменить иконки на Mac
Mac

Как изменить иконки на Mac