Middleware do Mongoose: ganchos anteriores e posteriores
Você escreverá ganchos de middleware de documento e consulta para tarefas como aplicar hash às senhas antes de salvar ou registrar eventos após uma busca.
Middleware do Mongoose: ganchos anteriores e posteriores é uma aula grátis de MongoDB Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de MongoDB Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de MongoDB Academy inclui 4 aulas no total.
O Que É o Middleware do Mongoose?
O middleware do Mongoose (também chamado de ganchos) consiste em funções executadas antes ou depois de operações específicas, como save, find, updateOne, deleteOne e outras. Ele permite inserir lógica personalizada no ciclo de vida de operações de documentos e consultas sem sobrecarregar os controladores de rotas. Usos comuns incluem gerar hashes de senhas antes de salvar, registrar tempos de consulta, impor exclusões lógicas e preencher dados relacionados depois de find.
Dois Tipos: Middleware de Documento e de Consulta
O Mongoose possui duas categorias distintas de middleware: o middleware de documento se conecta a operações em uma instância específica de Document (save, validate, remove, init). O middleware de consulta se conecta a operações de consulta invocadas no Model (find, findOne, updateOne, deleteOne etc.). A principal diferença está à qual objeto this se refere: no middleware de documento, this é o documento; no middleware de consulta, this é o objeto de consulta.
// Document middleware: 'this' = the document
userSchema.pre('save', function () {
console.log('Saving document:', this.email);
});
// Query middleware: 'this' = the Query object
userSchema.pre('find', function () {
console.log('Running query:', this.getQuery());
});Pre-save: Gerando Hashes de Senhas
O gancho pre-save é o middleware de documento mais comum. Ele é executado antes de um documento ser salvo no MongoDB. O caso de uso clássico é gerar hashes de senhas: quando um documento de usuário é salvo com uma senha nova ou modificada, gere o hash com bcrypt antes de armazená-la. A verificação this.isModified('password') impede gerar novamente o hash de uma senha já protegida em salvamentos não relacionados.
const bcrypt = require('bcrypt');
userSchema.pre('save', async function () {
// 'this' is the User document being saved
if (!this.isModified('password')) {
return; // skip if password hasn't changed
}
const saltRounds = 12;
this.password = await bcrypt.hash(this.password, saltRounds);
// The hashed value replaces the plain password before MongoDB stores it
});Auxiliares isNew e isModified
O middleware de documento tem acesso a auxiliares de rastreamento: this.isNew é true quando o documento está sendo inserido pela primeira vez (e não atualizado). this.isModified(path) retorna true se o campo especificado tiver sido alterado desde que o documento foi salvo ou obtido pela última vez. Esses auxiliares permitem executar ganchos condicionalmente — somente na criação ou somente quando um campo específico for alterado.
userSchema.pre('save', async function () {
if (this.isNew) {
// Only runs when creating a new user, not on updates
this.verificationToken = crypto.randomBytes(32).toString('hex');
this.verificationExpires = new Date(Date.now() + 24 * 60 * 60 * 1000);
}
if (this.isModified('email')) {
// Only runs when the email field specifically changed
this.emailVerified = false; // reset verification on email change
}
});Post-save: Efeitos Colaterais Depois de Salvar
Ganchos pós-save são executados depois que um documento é persistido com sucesso. Eles recebem o documento salvo como primeiro argumento e, no Mongoose mais antigo, uma função de retorno de chamada next. Os ganchos pós-save são ideais para efeitos colaterais que devem ocorrer depois de um salvamento bem-sucedido: enviar um e-mail de boas-vindas, atualizar um índice de pesquisa, publicar um evento em uma fila de mensagens ou limpar um cache. Erros nos ganchos pós-save não desfazem o salvamento.
userSchema.post('save', async function (doc) {
// 'doc' is the saved document
if (doc.isNew) {
// Note: 'isNew' is false here (doc was just saved, so it's no longer new)
// Track this with a flag set in pre-save:
}
});
// Pattern: set a flag in pre-save, read it in post-save
userSchema.pre('save', function () {
this._wasNew = this.isNew; // save state before it changes
});
userSchema.post('save', async function (doc) {
if (doc._wasNew) {
await sendWelcomeEmail(doc.email, doc.name);
await analyticsTracker.track('user_created', { userId: doc._id });
}
});Middleware de consulta: pré-find para exclusões lógicas
Um padrão clássico de middleware de consulta é implementar exclusões lógicas. Em vez de remover os documentos, defina o campo deletedAt. Em seguida, adicione um gancho pre('find') que inclua automaticamente { deletedAt: null } em todas as consultas find, garantindo que os documentos excluídos nunca sejam retornados por padrão. Isso fornece uma trilha de auditoria e, ao mesmo tempo, deixa a lógica de exclusão lógica transparente para o restante da aplicação.
const postSchema = new mongoose.Schema({
title: String,
content: String,
deletedAt: { type: Date, default: null }
});
// Automatically exclude soft-deleted documents from all find queries
postSchema.pre(/^find/, function () {
// 'this' is the Query object
this.where({ deletedAt: null });
// /^find/ matches find, findOne, findOneAndUpdate, etc.
});
// Now Post.find({}) never returns deleted posts
// To explicitly query deleted posts, you'd call Post.find({}).bypassMiddleware() or use .lean() with the native driverMiddleware de consulta: preenchimento automático pré-find
Você pode usar middleware de consulta para preencher automaticamente um campo de referência em cada find. Isso garante que um documento referenciado seja sempre resolvido sem exigir que os chamadores adicionem .populate() a cada consulta. Embora seja conveniente, tenha cuidado: o preenchimento automático adiciona uma segunda consulta a cada find e pode reduzir o desempenho se a referência for grande ou nem sempre for necessária.
const reviewSchema = new mongoose.Schema({
productId: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
rating: Number,
comment: String
});
// Always populate author info on find
reviewSchema.pre(/^find/, function () {
this.populate({
path: 'userId',
select: 'name avatar'
});
});
// Now Review.find() always includes user name and avatarPré-deleteOne: exclusões em cascata
O Mongoose não impõe exclusões em cascata automaticamente, ou seja, a exclusão de documentos relacionados quando um documento pai é excluído. Você pode implementar o comportamento em cascata usando middleware de documento. Um gancho pré-deleteOne em um modelo User pode excluir todas as publicações, todos os comentários e todas as sessões pertencentes a esse usuário antes que o próprio documento do usuário seja removido. Isso preserva a integridade referencial sem restrições de chave estrangeira.
userSchema.pre('deleteOne', { document: true, query: false }, async function () {
// 'this' is the User document being deleted
const userId = this._id;
// Cascade delete related documents
await Promise.all([
Post.deleteMany({ authorId: userId }),
Comment.deleteMany({ userId: userId }),
Session.deleteMany({ userId: userId }),
Notification.deleteMany({ userId: userId })
]);
console.log('Cascade deleted data for user:', userId);
});
// Trigger:
// const user = await User.findById(id);
// await user.deleteOne(); // triggers pre-deleteOne aboveTratamento de erros no middleware
Se uma função de gancho prévio lançar um erro ou rejeitar uma Promise, a operação que ela precede será cancelada. Isso permite realizar verificações de validação ou autorização no middleware e cancelar salvamentos ou consultas lançando um erro. Por exemplo, um gancho pré-save que valida regras de negócio, e não apenas a validação do esquema, pode lançar um erro que chega ao bloco catch da chamada .save() no código da aplicação.
orderSchema.pre('save', async function () {
if (this.total <= 0) {
throw new Error('Order total must be positive');
}
// Check inventory synchronously before saving the order
const product = await Product.findById(this.productId).lean();
if (!product || product.stock < this.quantity) {
throw new Error('Insufficient inventory for this order');
}
});
// In route handler:
try {
const order = new Order({ productId, quantity, total });
await order.save(); // throws if pre-save hook rejects
} catch (err) {
res.status(400).json({ error: err.message });
}Middleware de agregação
O Mongoose também oferece suporte a middleware para pipelines de agregação. Um gancho pré-agregação dá acesso ao vetor do pipeline antes que ele seja enviado ao MongoDB, permitindo adicionar etapas no início, como filtrar documentos excluídos, ou no final, como inserir um limite padrão. Acesse o pipeline por meio de this.pipeline() dentro da função do gancho.
postSchema.pre('aggregate', function () {
// 'this' is the Aggregate object
// Add a $match stage at the beginning to exclude soft-deleted documents
this.pipeline().unshift({
$match: { deletedAt: null }
});
});
// Now Post.aggregate([...]) automatically excludes deleted posts
// at the start of every aggregation pipelineArmadilhas do middleware: métodos de consulta que ignoram ganchos
Nem todas as operações de escrita acionam o middleware de documento. updateMany(), findOneAndUpdate() e replaceOne(), quando chamados no Model, e não em uma instância, ignoram os ganchos de documento save; eles são middleware de consulta e precisam de ganchos separados se você quiser interceptá-los. Por exemplo, um gancho pré-save de hash de senha NÃO é executado quando você chama User.updateOne({}, { $set: { password: plain } }). Sempre gere o hash no código da aplicação para atualizações baseadas em consultas.
// WRONG: password NOT hashed — bypasses pre-save hook
await User.updateOne({ _id: userId }, { $set: { password: plainPassword } });
// RIGHT for query-level updates: hash before calling updateOne
const hashed = await bcrypt.hash(plainPassword, 12);
await User.updateOne({ _id: userId }, { $set: { password: hashed } });
// Or: fetch, modify, save — triggers pre-save hook
const user = await User.findById(userId);
user.password = plainPassword; // hook will hash it
await user.save();Verificação rápida
Teste sua compreensão dos conceitos de MongoDB e bancos de dados NoSQL desta lição.
Recapitulação da lição
Nesta lição, você aprendeu que: os ganchos prévios são executados antes de uma operação e podem cancelá-la lançando um erro; os ganchos posteriores são executados depois e recebem o resultado como argumento; o middleware de documento (pré-save, pré-deleteOne) usa 'this' como o documento, enquanto o middleware de consulta usa 'this' como o objeto Query; e os métodos de escrita no nível da consulta (updateOne, updateMany, findOneAndUpdate) ignoram o middleware de documento — esteja sempre atento a quais ganchos são executados para cada tipo de operação. Isso conclui a trilha do curso de MongoDB e bancos de dados NoSQL!
Aprenda JavaScript com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 30
- Aulas
- 120
Perguntas Frequentes
A aula “Middleware do Mongoose: ganchos anteriores e posteriores” é grátis?
Sim — o texto completo de “Middleware do Mongoose: ganchos anteriores e posteriores” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de MongoDB Academy, atualize para CoddyKit PRO. O curso de MongoDB Academy inclui 4 aulas no total.
O que vou aprender em “Middleware do Mongoose: ganchos anteriores e posteriores”?
Você escreverá ganchos de middleware de documento e consulta para tarefas como aplicar hash às senhas antes de salvar ou registrar eventos após uma busca. Você pratica MongoDB Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar MongoDB Academy?
Nenhuma experiência prévia é necessária. MongoDB Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Middleware do Mongoose: ganchos anteriores e posteriores”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de MongoDB Academy?
Sim. Cada aula de MongoDB Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Conectando-se com o driver oficial do Node.js
- Esquemas, modelos e virtuais do Mongoose
- Consultas do Mongoose, encadeamento e documentos enxutos
- Middleware do Mongoose: ganchos anteriores e posteriores