Node.js: MongoDB com Node.js
Última atualização: 2026-08-26
1. Por que escolher o MongoDB
A API de comércio eletrônico da Alice enfrenta um problema complicado: os atributos dos produtos variam muito entre as diferentes categorias — os laptops têm campos de CPU e memória, as roupas têm campos de tamanho e cor, e os alimentos têm campos de data de validade. Se fosse utilizado um banco de dados relacional, a estrutura da tabela teria que ser modificada toda vez que uma nova categoria fosse adicionada. O modelo de documentos do MongoDB suporta naturalmente registros com estruturas diferentes; cada documento de produto pode ter campos completamente diferentes, sem a necessidade de qualquer migração de esquema.
- O MongoDB é um banco de dados baseado em documentos que armazena dados no formato BSON, que é semelhante ao JSON, mas suporta mais tipos de dados.
- O modelo de documento se alinha naturalmente ao modelo de objetos do Node.js, reduzindo a sobrecarga das conversões ORM
- Não há um esquema fixo; os campos podem ser adicionados ou removidos dinamicamente à medida que o negócio evolui
- Forte escalabilidade horizontal, adequada para cenários de leitura e gravação com alta simultaneidade
- Uma ampla variedade de operadores de consulta e mecanismos de indexação para atender a requisitos complexos de pesquisa
| Conceitos de SQL | Conceitos do MongoDB | Descrição |
|---|---|---|
| Banco de dados | Banco de dados | Banco de dados, terminologia consistente |
| Tabela | Coleção | Tabela → Coleção |
| Linha | Documento | Linha → Documento |
| Coluna | Campo | Coluna → Campo |
| Chave primária | _id (ObjectId) |
Chave primária gerada automaticamente |
| JOIN | $lookup (Agregação) |
Diferentes tipos de consultas de junção |
| Esquema | Sem esquema obrigatório | Regra de validação opcional |
| Índice | Índice | Mecanismos de indexação semelhantes |
2. Instalação e conexão
(1) Instale o driver do MongoDB
Use o pacote oficial mongodb do npm para se conectar a um servidor MongoDB.
▶ Exemplo: Instalação de um driver
npm install mongodb
(2) Estabelecer uma conexão com o cliente
MongoClient é o ponto de entrada para se conectar ao MongoDB; ele especifica o endereço e as opções por meio de uma string de conexão.
▶ Exemplo: Conexão básica
const { MongoClient } = require('mongodb');
const uri = 'mongodb://localhost:27017';
const client = new MongoClient(uri);
async function connect() {
try {
await client.connect();
console.log('Connected to MongoDB');
const db = client.db('myapp');
return db;
} catch (err) {
console.error('Connection failed:', err.message);
process.exit(1);
}
}
(3) Pools de conexão e opções de configuração
MongoClient Pool de conexões integrado; o tamanho e o comportamento do pool são controlados por meio de opções.
▶ Exemplo: Conexão com configuração de pool de conexões
const client = new MongoClient(uri, {
maxPoolSize: 10,
minPoolSize: 2,
maxIdleTimeMS: 30000,
serverSelectionTimeoutMS: 5000,
connectTimeoutMS: 10000,
});
| Opção de configuração | Valor padrão | Descrição |
|---|---|---|
maxPoolSize |
100 | Número máximo de conexões no pool de conexões |
minPoolSize |
0 | Número mínimo de conexões no pool de conexões |
maxIdleTimeMS |
0 | Tempo limite de conexão inativa (0 = sem tempo limite) |
serverSelectionTimeoutMS |
30000 | Tempo limite para seleção do servidor expirou |
connectTimeoutMS |
30000 | Tempo limite para o estabelecimento da conexão expirou |
socketTimeoutMS |
0 | Tempo limite do soquete |
retryWrites |
true | Repetir automaticamente as operações de gravação |
(4) Encerramento adequado das conexões
Quando o aplicativo for encerrado, ele deve fechar as conexões e liberar os recursos.
▶ Exemplo: Desligamento controlado
process.on('SIGINT', async () => {
await client.close();
console.log('MongoDB connection closed');
process.exit(0);
});
3. Operações CRUD
(1) Inserir um documento
Use insertOne para inserir um único documento e insertMany para inserir vários documentos.
▶ Exemplo: Inserção da documentação do produto
const products = db.collection('products');
const result = await products.insertOne({
name: 'Mechanical Keyboard',
price: 89.99,
category: 'electronics',
specs: { switches: 'Cherry MX Blue', layout: 'ANSI' },
createdAt: new Date(),
});
console.log('Inserted ID:', result.insertedId);
(2) Pesquise na documentação
find Retorna o cursor; findOne retorna um único documento.
▶ Exemplo: Consulta de produtos
const product = await products.findOne({ category: 'electronics' });
console.log(product);
const cursor = products.find({ price: { $gt: 50 } });
const expensive = await cursor.toArray();
console.log(`${expensive.length} products found`);
(3) Atualizar a documentação
updateOne Atualiza a primeira correspondência; updateMany atualiza todas as correspondências.
▶ Exemplo: Atualização dos preços dos produtos
const updateResult = await products.updateOne(
{ name: 'Mechanical Keyboard' },
{ $set: { price: 79.99, updatedAt: new Date() } },
);
console.log('Modified count:', updateResult.modifiedCount);
(4) Excluir um documento
deleteOne exclui a primeira ocorrência; deleteMany exclui todas as ocorrências.
▶ Exemplo: Exclusão de um produto
const deleteResult = await products.deleteOne({
name: 'Mechanical Keyboard',
});
console.log('Deleted count:', deleteResult.deletedCount);
(5) Referência rápida aos métodos CRUD
| Operação | Método | Valor de retorno | Descrição |
|---|---|---|---|
| Inserir um único registro | insertOne(doc) |
{insertedId} |
Retornar o ID gerado automaticamente |
| Inserir vários | insertMany([doc]) |
{insertedIds, insertedCount} |
Inserção em lote |
| Consultar um único registro | findOne(filter) |
Documento ou nulo | Retornar a primeira correspondência |
| Consultar várias linhas | find(filter) |
Cursor | Requer toArray() ou iteração |
| Atualizar uma única entrada | updateOne(filter, update) |
{modifiedCount} |
Atualizar apenas a primeira entrada |
| Atualizar várias entradas | updateMany(filter, update) |
{modifiedCount} |
Atualizar todas as correspondências |
| Excluir uma única entrada | deleteOne(filter) |
{deletedCount} |
Excluir a primeira ocorrência |
| Excluir vários | deleteMany(filter) |
{deletedCount} |
Excluir todas as ocorrências |
| Substituir no documento | replaceOne(filter, doc) |
{modifiedCount} |
Substituir em todo o documento |
4. ObjectId e operadores de consulta
(1) O mecanismo ObjectId
O valor padrão _id para cada documento é do tipo ObjectId; a codificação de 12 bytes inclui um carimbo de data e hora, um identificador da máquina e um contador.
▶ Exemplo: Como usar o ObjectId
const { ObjectId } = require('mongodb');
const id = new ObjectId();
console.log('ID string:', id.toHexString());
console.log('Timestamp:', id.getTimestamp());
const product = await products.findOne({
_id: new ObjectId('6850a1b2c3d4e5f6a7b8c9d0'),
});
(2) Operadores de comparação
▶ Exemplo: Consulta de comparação
const expensive = await products.find({ price: { $gt: 100 } }).toArray();
const cheap = await products.find({ price: { $lt: 20 } }).toArray();
const midRange = await products.find({ price: { $gte: 50, $lte: 100 } }).toArray();
(3) Operadores lógicos e de conjuntos
▶ Exemplo: consultas com $in e $or
const selected = await products.find({
category: { $in: ['electronics', 'books'] },
}).toArray();
const mixed = await products.find({
$or: [
{ price: { $lt: 10 } },
{ category: 'electronics' },
],
}).toArray();
(4) Consultas com expressões regulares
▶ Exemplo: Expressão regular para identificar nomes de produtos
const matched = await products.find({
name: { $regex: /^Mechanical/i },
}).toArray();
(5) Referência rápida para operadores de consulta
| Operador | Sintaxe | Descrição |
|---|---|---|
$eq |
{field: {$eq: val}} |
é igual a (e {field: val}) |
$gt |
{field: {$gt: val}} |
Maior que |
$gte |
{field: {$gte: val}} |
Maior ou igual a |
$lt |
{field: {$lt: val}} |
menor que |
$lte |
{field: {$lte: val}} |
Menor ou igual a |
$ne |
{field: {$ne: val}} |
Não é igual a |
$in |
{field: {$in: [v1,v2]}} |
Dentro da matriz |
$nin |
{field: {$nin: [v1,v2]}} |
Não consta na matriz |
$or |
{$or: [{...},{...}]} |
ou condição |
$and |
{$and: [{...},{...}]} |
Condições |
$not |
{field: {$not: {...}}} |
Desmarcar |
$regex |
{field: {$regex: 'pattern'}} |
Correspondência de expressão regular |
$exists |
{field: {$exists: true}} |
Esse campo existe? |
5. Projeção e classificação
(1) Campo de retorno do controle de projeção
A projeção especifica quais campos devem ser retornados ou excluídos, reduzindo assim o tráfego de rede.
▶ Exemplo: Consulta de projeção
const names = await products.find(
{},
{ projection: { name: 1, price: 1, _id: 0 } },
).toArray();
const withoutSpecs = await products.find(
{},
{ projection: { specs: 0, createdAt: 0 } },
).toArray();
(2) Ordenação e paginação
sort ordena os resultados, enquanto skip e limit implementam a paginação.
▶ Exemplo: Ordenação e paginação
const page = 2;
const pageSize = 10;
const sorted = await products.find({})
.sort({ price: -1, name: 1 })
.skip((page - 1) * pageSize)
.limit(pageSize)
.toArray();
1indica ordem crescente;-1indica ordem decrescente- A ordenação por vários campos ocorre na ordem em que são declarados
skipO desempenho é insatisfatório quando há muitos registros; para conjuntos de dados grandes, recomenda-se usar consultas por intervalo.
6. Noções básicas sobre índices
(1) Criar um índice
Os índices aceleram as consultas, mas aumentam a sobrecarga de gravação e o espaço de armazenamento.
▶ Exemplo: Criação de um índice
await products.createIndex({ name: 1 });
await products.createIndex({ category: 1, price: -1 });
await products.createIndex({ name: 'text' });
const indexes = await products.indexes();
console.log(indexes);
(2) Índices exclusivos e índices compostos
▶ Exemplo: Índice exclusivo
await products.createIndex({ sku: 1 }, { unique: true });
- O MongoDB cria automaticamente um índice exclusivo para
_id - Os índices compostos seguem o princípio do prefixo mais à esquerda
- Os índices de texto oferecem suporte à pesquisa de texto completo; apenas um por coleção
7. Processo de conexão e operação do MongoDB
flowchart TD
A[App Launch] --> B[Create MongoClient]
B --> C[client.connect]
C -->|Success| D[Get db Examples]
C -->|Failure| E[Error Handling/Retry]
E --> C
D --> F[Get collection]
F --> G{CRUD Operation}
G -->|Write| H[insertOne / insertMany]
G -->|Read| I[find / findOne]
G -->|Update| J[updateOne / updateMany]
G -->|Delete| K[deleteOne / deleteMany]
H --> L[Back insertedId]
I --> M[Back to the Document/Cursor]
J --> N[Back modifiedCount]
K --> O[Back deletedCount]
L --> P{Continue?}
M --> P
N --> P
O --> P
P -->|Yes| G
P -->|No| Q[client.close]
Q --> R[Exit the app]
8. Exemplo abrangente: Camada de acesso a dados de gerenciamento de produtos
const { MongoClient, ObjectId } = require('mongodb');
class ProductRepository {
constructor(uri, dbName) {
this.client = new MongoClient(uri, {
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
});
this.dbName = dbName;
this.collection = null;
}
async connect() {
await this.client.connect();
const db = this.client.db(this.dbName);
this.collection = db.collection('products');
await this.collection.createIndex({ name: 1 });
await this.collection.createIndex({ category: 1, price: -1 });
console.log('ProductRepository connected');
}
async create(productData) {
const doc = {
...productData,
createdAt: new Date(),
updatedAt: new Date(),
};
const result = await this.collection.insertOne(doc);
return { ...doc, _id: result.insertedId };
}
async findById(id) {
return await this.collection.findOne({
_id: new ObjectId(id),
});
}
async findByCategory(category, page = 1, pageSize = 10) {
const skip = (page - 1) * pageSize;
const [items, total] = await Promise.all([
this.collection.find({ category })
.sort({ price: -1 })
.skip(skip)
.limit(pageSize)
.project({ name: 1, price: 1, category: 1 })
.toArray(),
this.collection.countDocuments({ category }),
]);
return { items, total, page, pageSize };
}
async update(id, updates) {
const result = await this.collection.updateOne(
{ _id: new ObjectId(id) },
{ $set: { ...updates, updatedAt: new Date() } },
);
return result.modifiedCount > 0;
}
async delete(id) {
const result = await this.collection.deleteOne({
_id: new ObjectId(id),
});
return result.deletedCount > 0;
}
async disconnect() {
await this.client.close();
console.log('ProductRepository disconnected');
}
}
async function main() {
const repo = new ProductRepository(
'mongodb://localhost:27017',
'ecommerce',
);
try {
await repo.connect();
const created = await repo.create({
name: 'Wireless Mouse',
price: 29.99,
category: 'electronics',
specs: { dpi: 16000, buttons: 6 },
});
console.log('Created:', created._id);
const found = await repo.findById(created._id);
console.log('Found:', found.name);
await repo.update(created._id, { price: 24.99 });
const page = await repo.findByCategory('electronics', 1, 10);
console.log('Page items:', page.items.length);
await repo.delete(created._id);
console.log('Deleted');
} finally {
await repo.disconnect();
}
}
main().catch(console.error);
❓ Perguntas Frequentes
P: Por que usar o MongoDB em vez do MySQL? R: Quando as estruturas de dados mudam com frequência e os campos não são fixos, o modelo de documentos do MongoDB pode se adaptar sem exigir modificações nas tabelas; se o negócio envolver um grande número de transações e relações complexas, o MySQL é mais adequado.
P: O que é o ObjectId? R: O ObjectId é um identificador único de 12 bytes gerado automaticamente pelo MongoDB. Os primeiros 4 bytes correspondem a um carimbo de data/hora; é possível recuperar a hora de criação usando
id.getTimestamp()sem a necessidade de campos adicionais.
P: Como deve ser definido o tamanho do pool de conexões? R: Geralmente, defina-o entre 5 e 10 vezes o número de núcleos da CPU (maxPoolSize); para aplicativos com uso intensivo de E/S, ele pode ser aumentado adequadamente. Defina minPoolSize entre 2 e 5 para evitar atrasos na inicialização a frio.
P: Como devo lidar com falhas de conexão? R: Defina o parâmetro
serverSelectionTimeoutMSpara limitar o tempo de espera, registre o erro no blococatche implemente uma degradação gradual; em um ambiente de produção, recomendamos o uso de um mecanismo de repetição de tentativas e verificações de integridade.
P: Para quais cenários o MongoDB é adequado? R: Ele é adequado para cenários que exigem esquemas flexíveis e operações frequentes de leitura/gravação, como gerenciamento de conteúdo, análise de logs, dados de IoT e catálogos de produtos; não é adequado para sistemas financeiros essenciais que exigem forte consistência transacional.
P: É possível misturar 1s e 0s na projeção? R: Exceto pelo
_id, não é possível misturá-los — use apenas 1s para incluir os campos especificados ou apenas 0s para excluí-los; o_idé retornado por padrão e pode ser definido como 0 individualmente para excluí-lo.
9. Exercícios
- Escreva um script para se conectar a uma instância local do MongoDB e inserir 5 documentos de produtos de categorias diferentes, cada um contendo pelo menos 3 campos diferentes.
- Implementar uma consulta por faixa de preço ($gte/$lte) e ordenar os resultados em ordem decrescente por preço, retornando apenas os campos “nome” e “preço”
- Use a combinação
$ine$regexpara pesquisar produtos cuja categoria conste na lista especificada e cujo nome contenha uma palavra-chave específica - Crie índices compostos para campos consultados com frequência e use
explain()para comparar as diferenças nos planos de execução antes e depois da indexação. - Crie uma função genérica de consulta paginada que aceite os parâmetros filter, projection, sort, page e pageSize
📖 Resumo
- 1 Por que escolher o MongoDB: conceitos básicos e uso
- 2 Conceitos-chave e uso da instalação e conexão
- 3 Conceitos fundamentais e uso das operações CRUD
- 4 Conceitos fundamentais e uso do ObjectId e dos operadores de consulta
- 5 Conceitos fundamentais e uso de projeções e ordenação
- 6 Conceitos fundamentais e uso dos princípios básicos de indexação
- 7 Conceitos fundamentais e uso das conexões e operações do MongoDB
- 8 Exemplo abrangente: conceitos fundamentais e uso da camada de acesso a dados na gestão de produtos
📝 Exercícios
- Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
- Modifique o exemplo completo e adicione suas próprias extensões
- Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
- Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
- Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.