Conectando Node.js ao MongoDB com Mongoose

Rocketseat

Rocketseat

8 min de leitura
conectar-nodejs-mongodb-mongoose-tutorial
Se você desenvolve com Node.js, sabe que a flexibilidade do MongoDB é uma grande vantagem. Contudo, gerenciar dados de forma consistente nesse ambiente pode se tornar um desafio. É aqui que entra o Mongoose, uma ferramenta que serve como ponte entre seu código e o banco de dados, equilibrando a liberdade do NoSQL com a estrutura e a previsibilidade que aplicações robustas exigem.
👉
Se você caiu aqui de paraquedas e quer um vídeo de como sair do zero em node.js, eu tenho a recomendação certa para você!
Video preview

Por que Mongoose?

O Mongoose é uma biblioteca de Object Data Modeling (ODM). Ele converte os objetos JavaScript, com os quais você já trabalha no Node.js, para o formato de documentos que o MongoDB entende, e faz o caminho inverso quando você busca informações. A própria documentação descreve o Mongoose como uma forma de modelagem de objetos elegante para Node.js.
Embora seja possível trabalhar com o MongoDB sem um ODM, o valor do Mongoose está na simplificação da modelagem de dados, na aplicação de validações e na manipulação geral das informações. O verdadeiro ganho está na melhoria da developer experience (experiência do desenvolvedor). Ele permite que a pessoa que programa pense em termos de objetos e classes, um paradigma mais intuitivo do que construir queries manualmente. A interação com o banco de dados, por meio de comandos como new User() ou user.save(), passa a usar a mesma sintaxe e lógica do restante da aplicação, diminuindo a carga cognitiva e acelerando o desenvolvimento.

Schemas e Models

Schema: é a planta baixa, o contrato que dita a estrutura dos documentos dentro de uma collection. É no Schema que você define os campos, seus tipos de dados (como String, Number, Date, Boolean, Array e outros) e as regras que eles devem seguir.
Veja um exemplo de um Schema para um post de blog:
import mongoose from 'mongoose'; const { Schema } = mongoose; const postSchema = new Schema({ title: String, author: String, body: String, published: Boolean, meta: { votes: Number, favs: Number }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } });
Model: é a interface que permite a interação direta com a collection do banco de dados. É por meio do Model que as operações de Criar, Ler, Atualizar e Deletar (CRUD) são executadas. Uma convenção importante do Mongoose é que, ao chamar mongoose.model('Post', postSchema), a biblioteca automaticamente procura pela collection posts (o nome do modelo no plural e em minúsculas) no banco de dados.

Preparando o ambiente

Esta seção é um guia para configurar um projeto Node.js, deixando-o pronto para a conexão com o banco de dados.
Iniciando o projeto e instalando as dependências:
No seu terminal, execute os seguintes comandos para criar um diretório para o projeto, inicializá-lo e instalar as dependências:
mkdir api-mongodb-rocketseat cd api-mongodb-rocketseat npm init -y npm install mongoose dotenv
Instalamos dois pacotes: mongoose, para a interação com o MongoDB, e dotenv, para o gerenciamento de variáveis de ambiente. Para usar uma sintaxe mais moderna, adicione a seguinte linha ao seu package.json. Isso habilita o uso de ES Modules (import/export), melhorando a organização do código.
A importância das variáveis de ambiente com.env:
Expor credenciais, como a string de conexão do banco, diretamente no código é um risco de segurança. A solução é usar variáveis de ambiente. Crie um arquivo chamado .env na raiz do seu projeto e adicione a sua string de conexão:
#.env MONGO_URI=SUA_STRING_AQUI
O uso de um arquivo .env vai além da segurança, pois promove portabilidade e flexibilidade. Sua aplicação pode se conectar a diferentes bancos (desenvolvimento, testes, produção) apenas alterando o valor dessa variável, sem precisar modificar o código.
Configurando o MongoDB Atlas ou banco local:
Para obter uma string de conexão, você pode usar o MongoDB Atlas, um serviço de banco de dados na nuvem. O processo envolve criar um cluster gratuito, configurar um usuário e senha para o banco e liberar o acesso por IP.
Como alternativa, para um banco de dados rodando localmente, a string de conexão seria:
mongodb://127.0.0.1:27017/nome-do-banco
É recomendado usar 127.0.0.1 em vez de localhost. Versões recentes do Node.js podem tentar resolver localhost para um endereço IPv6, o que pode causar falhas de conexão se o seu MongoDB não estiver configurado para isso.
A string de conexão geralmente segue este formato:
protocolo://usuario:senha@host/banco-de-dados?opcoes

A abordagem moderna

Este é o coração do processo. Vamos focar em estabelecer e gerenciar a conexão de forma profissional, com código limpo e modular.
Criando um módulo de conexão dedicado - (clique para expandir):
A lógica de conexão não deve ficar no arquivo principal da aplicação. A melhor prática é isolá-la em um módulo próprio, como config/database.js. Essa separação de responsabilidades melhora a organização, a manutenibilidade e a testabilidade do código, pois facilita a criação de mocks do banco de dados em testes.
A conexão com async/await e tratamento de erros - (clique para expandir):
Dentro do módulo de conexão, usamos async/await com um bloco try/catch para um tratamento de erros claro e eficaz.
// config/database.js import mongoose from 'mongoose'; import 'dotenv/config'; const connectDB = async () => { try { await mongoose.connect(process.env.MONGO_URI); console.log('Conexão com o MongoDB estabelecida com sucesso!'); } catch (error) { console.error('Erro ao conectar com o MongoDB:', error.message); // Encerra a aplicação em caso de falha na conexão process.exit(1); } }; export default connectDB;
A chamada a process.exit(1) no catch é uma prática defensiva que impede a aplicação de continuar rodando em um estado inválido se a conexão com o banco falhar.
Gerenciando o ciclo de vida da conexão - (clique para expandir):
A conexão com o banco de dados tem um ciclo de vida. Monitorar eventos como desconexões e reconexões é muito importante em produção. O Mongoose emite esses eventos por meio do objeto mongoose.connection.
// Adicionar ao final do arquivo config/database.js mongoose.connection.on('connected', () => { console.log('Mongoose conectado ao DB.'); }); mongoose.connection.on('error', (err) => { console.error('Erro na conexão do Mongoose: ' + err); }); mongoose.connection.on('disconnected', () => { console.log('Mongoose desconectado.'); });

Da teoria à prática

Com a conexão pronta, vamos criar o modelo de dados que usaremos nos exemplos de CRUD.
Criando o Schema com validações - (clique para expandir):
Crie uma pasta models e, dentro dela, um arquivo User.js. Este Schema de usuário será mais completo, incorporando validações do Mongoose.
// models/User.js import mongoose from 'mongoose'; const { Schema } = mongoose; const userSchema = new Schema({ name: { type: String, required: true, trim: true // Remove espaços em branco no início e no fim }, email: { type: String, required: true, unique: true, // Garante que o email seja único na collection lowercase: true, // Converte o email para minúsculas antes de salvar trim: true }, password: { type: String, required: true, minlength: 6 // Define um comprimento mínimo para a senha }, createdAt: { type: Date, default: () => Date.now(), // Define um valor padrão immutable: true // Torna o campo imutável após a criação }, updatedAt: { type: Date, default: () => Date.now() } }); const User = mongoose.model('User', userSchema); export default User;
Cada opção (required, unique, lowercase, default, immutable, minlength) adiciona uma camada de consistência e segurança aos seus dados.
Compilando o Schema em um Model - (clique para expandir):
A linha const User = mongoose.model('User', userSchema); compila a "planta baixa" (Schema) em um Model, que é a classe que sua aplicação usará para interagir com os dados dos usuários.

Realizando operações CRUD com Mongoose

Esta seção tem exemplos de código para cada operação CRUD. Todos usam async/await e nomes fictícios.
Create - (clique para expandir):
Existem duas formas principais de criar documentos. A primeira, mais explícita, é instanciar um objeto do modelo e chamar .save().
const newUser = new User({ name: 'Laís', email: 'lais@rocketseat.team', password: 'senha_super_segura' }); await newUser.save();
A segunda forma, com .create(), é um atalho que instancia e salva em um único passo.
const user = await User.create({ name: 'Diego', email: 'diego@rocketseat.team', password: 'outra_senha_segura' });
Read - (clique para expandir):
Para ler dados, os métodos mais comuns são:
  • User.find({}): busca todos os documentos que correspondem a um filtro (um objeto vazio busca todos).
  • User.findOne({ email: 'diego@rocketseat.team' }): busca o primeiro documento que corresponde ao filtro.
  • User.findById('id_do_documento'): busca um documento pelo seu _id.
Para otimizar suas consultas, use projeção para selecionar apenas os campos que você realmente precisa.
Update - (clique para expandir):
O método findOneAndUpdate é a forma moderna e atômica de encontrar e atualizar um documento. A opção { new: true } é importante, pois faz com que o método retorne o documento já com as alterações aplicadas.
const updatedUser = await User.findOneAndUpdate( { email: 'lais@rocketseat.team' }, { name: 'Laís Silva' }, { new: true } );
Delete - (clique para expandir):
Para remover documentos, você pode usar deleteOne ou findOneAndDelete.
await User.deleteOne({ email: 'diego@rocketseat.team' });
Em todas as operações de busca e modificação, considere usar o método .exec(). Embora await User.find() funcione, as queries do Mongoose não são promises completas. Chamar await User.find().exec() retorna uma promise real e fornece stack traces mais detalhados em caso de erro, uma prática que pode economizar tempo de depuração.

Tópicos avançados

Para levar sua aplicação a outro nível, vamos abordar tópicos cruciais para performance e manutenibilidade.
Otimizando sua conexão - (clique para expandir):
O método mongoose.connect() aceita um objeto de opções para "afinar" o comportamento da conexão.
Opção:
O que faz:
Quando usar:
maxPoolSize
Define o número máximo de conexões abertas no pool.
Aumente se sua aplicação tem muitas operações concorrentes e lentas.
minPoolSize
Define o número mínimo de conexões a serem mantidas abertas.
Aumente para evitar a latência de abrir novas conexões em apps com picos de uso.
serverSelectionTimeoutMS
Tempo (ms) que o driver tenta encontrar um servidor antes de dar erro.
Diminua (ex: 5000) para falhar mais rápido em desenvolvimento. Mantenha o padrão (30000) em produção com replica sets.
socketTimeoutMS
Tempo (ms) que um socket pode ficar inativo antes de ser fechado.
Ajuste se você tem operações muito longas que excedem o timeout padrão.
Middleware do Mongoose - (clique para expandir):
Os hooks de middleware pre e post são ferramentas poderosas para executar lógica em pontos específicos do ciclo de vida de um documento, como antes de salvar. Um ótimo exemplo é usar um pre('save') para atualizar automaticamente o campo updatedAt.
// Adicionar ao User.js userSchema.pre('save', function(next) { this.updatedAt = new Date(); next(); });
👉
E para você, que já está construindo aplicações robustas com Node.js, há um próximo passo inevitável na sua evolução: a inteligência artificial. A IA não é mais o futuro, é a ferramenta que está redefinindo o presente do desenvolvimento. Para entender como essa revolução acontece na prática e como ela pode acelerar sua carreira, assista ao vídeo especial que preparamos:
Video preview

Conclusão

Você aprendeu não apenas a conectar uma aplicação Node.js ao MongoDB, mas a fazê-lo de forma segura, organizada e pronta para escalar. O domínio da interação com o banco de dados é um passo de grande importância na sua evolução contínua como profissional de tecnologia.
Para solidificar o conhecimento, experimente implementar um CRUD completo para uma nova entidade ou explore a documentação oficial do Mongoose para descobrir mais validadores e opções de query.
E se você quer aprender tudo sobre Node.js, dominando uma linguagem flexível e popular para construir arquiteturas modernas, eficientes e escaláveis, confira nossa formação completa:
E chegamos ao fim! Esperamos ter ajudado e torcemos que o conteúdo tenha sido útil para você. Se pintar qualquer outra dúvida, já sabe: é só chamar a gente na comunidade.
Artigos_

Explore conteúdos relacionados

Descubra mais artigos que complementam seu aprendizado e expandem seu conhecimento.