Para usar o MongoDB no Node.js, instale o driver oficial (npm install mongodb), conecte com new MongoClient(uri), escolha um banco com client.db('name') e uma collection com db.collection('name'). Toda operação (insertOne, find, updateOne, deleteOne) retorna uma promise, então você dá await. Os documentos entram e saem como objetos JavaScript comuns.
Ele imprime [ 'Dune', 'Neuromancer', 'Hyperion' ]. O banco shop e a collection books não existiam antes: o MongoDB cria os dois no primeiro insert, e toArray() carrega todos os documentos para os quais o cursor do find() aponta.
Buscar documentos com filtros e projection
Um filtro é um objeto que descreve os documentos que você quer. Valores simples batem exatamente; operadores como $gt, $lt, $in e $regex ficam em um objeto aninhado. A projection do segundo argumento escolhe quais campos voltam.
recent guarda os dois livros mais novos depois de 1960, Hyperion e depois Neuromancer, só com title e year; findOne retorna o primeiro resultado (ou null), e countDocuments conta 2 livros que custam 10 ou mais. Troque $gt: 1960 por $gt: 1985 e só Hyperion sobra.
Atualizar e apagar
updateOne(filter, update) altera o primeiro documento que corresponde. O update usa operadores: $set substitui campos, $inc soma a um número, $unset remove um campo e $push adiciona a um array. updateMany e deleteMany agem em todos os que correspondem.
O update encontrou e alterou um documento, então Dune agora custa 10 com 2 em estoque, e apagar o livro sem estoque deixa 1. Um update precisa usar operadores: passe { price: 10 } sem $set e o driver recusa (replaceOne é a chamada que substitui um documento inteiro).
ObjectId e índices
Todo documento recebe um _id; quando você não define um, o driver cria um ObjectId. Para encontrar um documento por um id que chegou como string (de uma URL, por exemplo), envolva com new ObjectId(id). Um índice deixa a busca por um campo rápida, e um índice único faz o MongoDB recusar duplicados.
Um ObjectId como string tem 24 caracteres hexadecimais, e buscar com a string pura não encontra nada, então aquela linha imprime null. O segundo insert viola o índice único e falha com o código 11000, que é como você detecta um "e-mail já cadastrado".
Usar MongoDB em uma API Express
Conecte uma vez quando o servidor sobe e compartilhe a collection; não abra uma conexão nova a cada requisição. O driver mantém um pool de conexões e as reutiliza.
GET /books retorna os dois livros, cada um com um _id. Copie um id para GET /books/<id> para buscar um único livro, ou envie POST /books com um body JSON como {"title": "Foundation", "year": 1951} e liste os livros de novo.
Um app real lê a connection string de process.env.MONGODB_URI em vez de escrevê-la no código.
Se você quer schemas e validação por cima do driver, o Mongoose (npm install mongoose) o envolve:
const Book = mongoose.model('Book', new mongoose.Schema({ title: String, year: Number }));
await Book.create({ title: 'Dune', year: 1965 });
Erros comuns
Esquecer o toArray(). find() retorna um cursor, não documentos. Imprimir mostra um objeto cursor; chame toArray(), ou percorra com for await.
Conectar a cada requisição. Cada new MongoClient abre o próprio pool. Crie um client na inicialização e reutilize em todo lugar.
Nunca fechar o client em um script. Um client aberto mantém o processo vivo depois que seu código termina. Chame await client.close() no fim dos scripts; um servidor o mantém aberto a vida inteira.
Perguntas frequentes
Devo usar o driver mongodb ou o Mongoose?
O driver é a camada oficial e mais fina: você trabalha com objetos simples e a linguagem de consulta do próprio MongoDB. O Mongoose adiciona schemas, validação e models por cima, o que ajuda quando você quer que todo documento seja verificado. Os dois falam com o mesmo banco, e o Mongoose usa o driver por baixo.
Preciso criar o banco ou a collection antes?
Não. O MongoDB cria os dois na primeira vez que você insere um documento neles. client.db('shop') e db.collection('books') só retornam referências.
Onde coloco a connection string?
Em uma variável de ambiente como MONGODB_URI, lida com process.env.MONGODB_URI. A string de um banco hospedado contém uma senha, então ela nunca deve ir para o repositório.
Por que o find não retorna documentos quando busco por id?
O _id é guardado como ObjectId, não como string, então { _id: '65f1...' } não encontra nada. Converta antes: { _id: new ObjectId(id) }.