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

Mongoose в Express: модель, валидация и CRUD

6 min read Базы данных Обновлено 13 Apr 2026
Mongoose в Express — модели, валидация и CRUD
Mongoose в Express — модели, валидация и CRUD

Ноутбук с наложенными логотипами MongoDB и JavaScript

Коротко о Mongoose

Mongoose — библиотека для Node.js, которая добавляет схемы и удобные API для работы с коллекциями MongoDB. Она помогает гарантировать согласованность данных и упрощает валидацию, методы модели, хуки и работу с транзакциями.

Определение в одну строку: Mongoose — это слой между вашим кодом и MongoDB, превращающий «свободные» документы в предсказуемые объекты с правилами.

Важно: Mongoose не отменяет необходимость проектирования модели данных — он помогает его реализовать и поддерживать.

Что вы узнаете из этого руководства

  • Как установить и подключить Mongoose в проект Express
  • Как определять схемы и модели (валидация, типы, дефолты, индексы)
  • Как выполнять CRUD-операции и работать с транзакциями
  • Лучшие практики, отладка и сценарии, где Mongoose может не подходить
  • Альтернативы и быстрый чек-лист для команды

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

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

npm install mongoose

Пример простого подключения (файл index.js):

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

mongoose.connect("mongodb://127.0.0.1:27017/example")
  .then(() => console.log("Connected to database successfully"))
  .catch(err => console.error("DB connection error:", err));

// Дополнительно: обработка событий соединения
mongoose.connection.on('disconnected', () => console.warn('MongoDB disconnected'));

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

  • При подключении к продакшен-кластерам используйте строку подключения из переменных окружения и не храните пароли в коде.
  • Можно передавать опции (если нужно): useNewUrlParser и useUnifiedTopology уже установлены по умолчанию в новых версиях, но иногда полезно явно указывать другие опции.
  • Для отказоустойчивости используйте replica set URI.

Создание схемы и модели: базовые элементы

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

Пример модели User (userModel.js):

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, "Name is required"],
    trim: true,
    maxlength: 100
  },
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    trim: true
  },
  age: {
    type: Number,
    validate: {
      validator: function (value) {
        return value > 0;
      },
      message: () => "Please enter a valid age"
    }
  }
}, {
  timestamps: true // createdAt и updatedAt
});

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

module.exports = User;

Пояснения по свойствам схемы:

  • required: обязательно для сохранения. Можно указать булево или сообщение об ошибке.
  • unique: создает уникальный индекс (обратите внимание: unique не является валидатором на уровне документа, это индекс базы данных).
  • default: можно задать значение по умолчанию.
  • enum: ограничивает набор допустимых значений.
  • trim/lowercase/uppercase: трансформации строки перед сохранением.
  • timestamps: автоматически добавляет createdAt и updatedAt.

Встроенные валидации и кастомные валидаторы

Mongoose поддерживает встроенные валидаторы (required, min/max, match для RegExp) и кастомные функции-валидаторы.

Пример кастомного валидатора email:

email: {
  type: String,
  required: true,
  match: [/^\S+@\S+\.\S+$/, 'Email is invalid']
}

Или с внешней библиотекой (например, validator.js):

const validator = require('validator');

email: {
  type: String,
  validate: {
    validator: (v) => validator.isEmail(v),
    message: 'Email is invalid'
  }
}

Виртуальные свойства, методы и статики

  • Виртуалы (virtuals) позволяют добавлять вычисляемые поля, которые не сохраняются в БД.
  • Методы (schema.methods) доступны экземплярам документов.
  • Статические методы (schema.statics) доступны самой модели.

Пример:

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

userSchema.methods.sayHello = function() {
  return `Hello, ${this.name}`;
};

userSchema.statics.findByEmail = function(email) {
  return this.findOne({ email });
};

Мидлвары (хуки)

Mongoose поддерживает pre/post хуки для операций save, validate, remove, updateOne и т.д. Это удобно для хеширования паролей, логирования и других побочных эффектов.

Пример хука для хеширования пароля (синхронный псевдокод):

userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await hashPassword(this.password);
  next();
});

Важно: не делайте тяжелых долгих операций в хуках без обработки ошибок и таймаутов.

CRUD: практические примеры и нюансы

Примечание: в примерах предполагается, что модель User импортирована: const User = require(‘./userModel’);

Создание документов

  1. Через новый экземпляр и save():
let user = new User({ name, email, age });
user.save()
  .then(() => console.log('User created successfully'))
  .catch(err => console.error(err));
  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 }
], { ordered: false })
  .then(result => console.log('Inserted:', result))
  .catch(err => console.error(err));

Советы:

  • Для больших вставок используйте bulkWrite или insertMany с ordered: false, чтобы ошибки отдельных документов не прерывали всю операцию.
  • Обрабатывайте ошибки дублей (E11000) при уникальных индексах.

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

  • Получить все документы:
User.find({})
  .then(data => console.log(data))
  .catch(err => console.error(err));
  • Фильтрация и операторы MongoDB:
User.find({ age: { $gte: 18 } })
  .then(data => console.log(data))
  .catch(err => console.error(err));
  • По id или по одному документу:
User.findById(id)
  .then(doc => console.log(doc))
  .catch(err => console.error(err));

User.findOne({ email: 'johnson@example.com' })
  .then(user => {
    if (!user) { /* not found */ }
  });

Оптимизации:

  • Для быстрых выборок без документов Mongoose-объектов используйте .lean() — вернёт plain JS-объекты и быстрее.
  • Используйте проекции ({ name: 1, email: 1 }) чтобы вернуть только необходимые поля.
  • Пагинация: skip/limit или лучше — курсоры и диапазонные запросы по индексируемым полям.

Обновление документов

  • findByIdAndUpdate:
User.findByIdAndUpdate(id, req.body, { new: true, runValidators: true }, (err, doc) => {
  if (err) { /* handle error */ }
  // doc — обновлённый документ
});

Параметры:

  • new: true — вернуть обновлённый документ

  • runValidators: true — запустить валидацию по схеме при update

  • useFindAndModify — опция устарела в новых версиях; используйте методы модели

  • updateOne/updateMany и $set, $inc и другие операторы обновления позволяют делать частичные изменения без перезаписи документа.

Удаление документов

User.findByIdAndDelete(id, (error, result) => {
  if (error) { /* handle error */ }
  // result — удалённый документ
});

Или deleteMany для массовых удалений.

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

Для атомарных операций на нескольких документах используйте транзакции MongoDB (требуется Replica Set).

Пример сессии и транзакции:

const session = await mongoose.startSession();
session.startTransaction();
try {
  await User.create([{ name: 'A' }], { session });
  await AnotherModel.updateOne({ _id: id }, { $inc: { count: 1 } }, { session });
  await session.commitTransaction();
} catch (err) {
  await session.abortTransaction();
  throw err;
} finally {
  session.endSession();
}

Важно: транзакции увеличивают задержку и нагрузку. Используйте их только когда необходимые гарантии целостности.

Производительность и индексы

  • Индексы — основной инструмент ускорения чтения. Создавайте индексы по полям, которые используются в фильтрах и сортировках.
  • Используйте compound-индексы для часто комбинируемых полей.
  • Мониторьте explain() запроса в MongoDB, чтобы понимать, какие индексы используются.
  • Для аналитики и больших агрегаций лучше использовать агрегат-пайплайны MongoDB или отдельные инструменты обработки данных.

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

userSchema.index({ email: 1 });

Логирование и обработка ошибок

  • Ловите ошибки подключения и регистрируйте их в логах.
  • Обрабатывайте ошибки валидации (ValidationError) и ошибки уникальности (E11000) отдельно, чтобы возвращать понятные ответы клиенту.

Пример базовой обработки ошибок:

try {
  await user.save();
} catch (err) {
  if (err.name === 'ValidationError') {
    // вернуть 400 и сообщение клиенту
  } else if (err.code === 11000) {
    // ошибочный дубль уникального индекса
  } else {
    // 500 и лог
  }
}

Когда Mongoose — не лучший выбор

  • Скрипты для миграции/ETL или аналитические задачи, где важна максимальная скорость и минимальные накладные расходы, иногда выгоднее делать с помощью нативного MongoDB-драйвера.
  • Очень динамичные документы без предсказуемой структуры — если у вас нет пользы от схем, Mongoose добавит излишнюю сложность.
  • Если требуется ORM для нескольких баз данных (SQL + NoSQL), рассмотрите универсальные решения.

Альтернативные инструменты

  • Официальный MongoDB Node.js Driver — тоньше и быстрее для простых операций, даёт полный контроль.
  • Prisma — ORM с поддержкой MongoDB в ранних версиях, ориентирован на типизацию и миграции (проверять совместимость версий).
  • TypeORM / MikroORM — альтернативы, если вам нужна единая модель данных для SQL и NoSQL.

Быстрый чек-лист по внедрению Mongoose в проект Express

Для разработчика:

  • Установил и настроил Mongoose через переменные окружения
  • Определил схемы с нужными полями, индексами и валидацией
  • Добавил runValidators при обновлениях
  • Использует .lean() там, где важна производительность

Для DevOps:

  • Использует реплика сет для транзакций
  • Настроил резервное копирование и мониторинг MongoDB
  • Управляет переменными окружения и секретами

Для QA:

  • Тесты валидации модели (валидные/невалидные данные)
  • Тесты ошибок уникального индекса
  • Интеграционные тесты с in-memory MongoDB или тестовым кластером

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

  • Представляйте Mongoose как контракт на данные: схема — контракт, модель — реализация.
  • Используйте валидацию на уровне модели для защиты бизнеса; не полагайтесь только на клиентскую валидацию.
  • Выбирайте транзакции только для критических операций согласованности.

Решение: когда выбрать Mongoose или драйвер

flowchart TD
  A[Нужно ли иметь схему?] -->|Да| B[Используйте Mongoose]
  A -->|Нет| C[Рассмотрите нативный драйвер]
  B --> D{Потребность в транзакциях}
  D -->|Да| E[Replica Set + транзакции]
  D -->|Нет| F[Стандартные CRUD + индексы]
  C --> G[Оптимизированные скрипты/аналитика]

Шпаргалка по часто используемым методам

  • create(doc)
  • insertMany(docs, options)
  • find(filter, projection)
  • findById(id)
  • findOne(filter)
  • findByIdAndUpdate(id, update, { new, runValidators })
  • findByIdAndDelete(id)
  • updateOne(filter, update)
  • deleteMany(filter)
  • startSession() / транзакции

Критерии приёмки

  • Приложение корректно подключается к MongoDB и восстанавливает соединение при разрыве.
  • Все модели имеют схему и ключевые поля индексированы.
  • Обновления проходят валидацию (runValidators=true).
  • Массовые операции обрабатывают ошибки дублей и возвращают понятные ответы.

Итог и рекомендации

Mongoose даёт баланс между гибкостью MongoDB и предсказуемостью схем. Он упрощает валидацию, добавляет методы и хуки, но добавляет уровень абстракции. Используйте Mongoose, если вам важны схемы, чистые модели и удобные API. Для простых или высокопроизводительных задач рассмотрите нативный драйвер.

Важно: планируйте индексы заранее, тестируйте обновления с runValidators и отслеживайте производительность.

Ключевые выводы:

  • Mongoose структурирует данные и упрощает CRUD в Express-приложениях.
  • Схемы, валидация, хуки и методы помогают поддерживать контроль над данными.
  • Транзакции доступны, но требуют Replica Set и дают дополнительные накладные расходы.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

Как полностью переустановить ChromeOS на Chromebook
Инструкции

Как полностью переустановить ChromeOS на Chromebook

Идеальные скриншоты в Windows
Инструкции

Идеальные скриншоты в Windows

Проверка скорости Wi‑Fi: тесты и устранение проблем
Wi‑Fi

Проверка скорости Wi‑Fi: тесты и устранение проблем

Виджеты и гаджеты для Windows 10
Windows

Виджеты и гаджеты для Windows 10

Коды BSOD Windows 10/11: как найти и исправить
Windows

Коды BSOD Windows 10/11: как найти и исправить

Ограничить чувствительную рекламу в Google
Конфиденциальность

Ограничить чувствительную рекламу в Google