Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA

Guia completo

MongoDB do zero: guia de banco de dados por documentos

Entenda documentos, coleções, BSON, CRUD, modelagem, aggregation e índices no MongoDB com uma livraria prática e um roteiro de estudo completo.

5 lições em trilha6 artigos no guiaAtualizado em agosto de 2026

MongoDB é um sistema de banco de dados que guarda registros como documentos, agrupados em coleções. Ao terminar este guia, você terá uma pequena livraria rodando, saberá ler a estrutura dos dados e terá um caminho claro para estudar CRUD, modelagem, aggregation e índices.

O nome técnico é banco de dados orientado a documentos. Em português direto: em vez de obrigar cada informação a caber primeiro numa linha dividida em colunas fixas, o MongoDB permite guardar um objeto inteiro, com listas e objetos internos, como uma unidade. Esse objeto é persistido em BSON, uma representação binária que amplia os tipos disponíveis no JSON.

Isso não significa “jogar qualquer objeto no banco”. Um bom documento tem uma fronteira pensada, tipos coerentes, regras para dados importantes e índices que acompanham as consultas reais. A flexibilidade tira uma obrigação do formato; ela não tira a responsabilidade de modelar.

Da sacola da livraria ao documento no banco

Imagine a Livraria Horizonte preparando um pedido para entrega. Dentro de uma pasta vão o endereço usado naquela compra, os itens, o preço de cada item, as quantidades, o total e o status. Quem abre a pasta encontra o retrato daquela venda sem buscar uma folha diferente para cada detalhe.

Esse é um bom paralelo para um documento MongoDB. A pasta é o documento; o nome escrito na lombada é o _id; os campos preenchidos são os pares de chave e valor; a lista de itens é um array; endereço é um documento incorporado. O arquivo com todas as pastas de pedidos é a coleção pedidos.

O paralelo tem limite. MongoDB não é um armário físico e não lê necessariamente uma pasta por vez. Ele executa filtros sobre conjuntos, usa índices, mantém cache, replica dados e pode distribuir coleções entre servidores. Além disso, colocar tudo na mesma “pasta” não é sempre correto: uma lista que cresce sem limite ou um dado compartilhado por milhares de registros pode pedir uma coleção separada e referências.

Veja como um pedido pode ficar. Não é uma sequência de tabelas montada na hora; é o agregado que a tela de confirmação costuma ler junto:

json
{
  "_id": "pedido-1",
  "clienteId": "cliente-1",
  "entrega": {
    "rua": "Rua das Flores",
    "numero": 120,
    "cidade": "Campinas"
  },
  "itens": [
    {
      "livroId": "livro-1",
      "titulo": "JavaScript do zero",
      "precoUnitario": 39.9,
      "quantidade": 2
    }
  ],
  "total": 79.8,
  "status": "pago"
}
Documento armazenado com 1 endereço incorporado e 1 item no array.

O clienteId é uma referência; o endereço e o item são cópias incorporadas. Essa mistura é intencional. O cadastro atual do cliente pode mudar, mas um pedido antigo deve continuar mostrando para onde foi enviado e quanto custou. Modelar MongoDB é decidir quais dados precisam viver e mudar juntos.

Servidor, database, coleção, documento e campo

No começo, é comum chamar tudo de “Mongo”. Separar os nomes evita confusão nas configurações e nos erros:

  • MongoDB Server é o processo mongod que recebe conexões e persiste dados;
  • database é um espaço lógico, como livraria;
  • collection agrupa documentos de um assunto, como livros;
  • document é um registro composto por campos e valores;
  • field é uma propriedade, como titulo, estoque ou editora.cidade;
  • MongoDB Node.js Driver é a biblioteca oficial usada pela aplicação para conversar com o servidor.

Uma coleção lembra uma tabela porque as duas agrupam registros do mesmo assunto. Mas a comparação para aí. Documentos de uma coleção podem não ter todos os mesmos campos, e um campo pode conter outro documento ou uma lista. É possível exigir formatos com validação, porém essa regra é uma decisão sua, não uma consequência automática de criar a coleção.

Todo documento precisa de _id, sua identidade única na coleção. Se você não enviar o campo, o driver normalmente cria um ObjectId. Nos exemplos deste cluster usamos ids de texto, como livro-1, para que as saídas possam ser repetidas e comparadas. Em um projeto, escolha o tipo de id uma vez e mantenha o contrato; alternar texto, número e ObjectId para a mesma entidade complica filtros e referências.

JSON na aparência, BSON de verdade

O documento parece JSON quando aparece no terminal, mas o formato armazenado é BSON. BSON suporta tipos que JSON puro não expressa diretamente, como ObjectId, Date, Decimal128, dados binários e inteiros de tamanhos específicos.

Essa diferença aparece quando uma API transforma o resultado em JSON. Um ObjectId ou Decimal128 precisa de uma representação textual compatível; uma data pode virar string ISO. Portanto, “parece um objeto JavaScript” não quer dizer “todos os tipos são iguais aos do JavaScript”. O driver faz a conversão entre os dois mundos, mas sua aplicação ainda precisa decidir o contrato que entrega ao navegador.

No laboratório, preco: 39.9 mantém a leitura simples. Em contabilidade real, o tipo é decisão de domínio: centavos inteiros evitam frações; Decimal128 guarda decimal de alta precisão; Number usa ponto flutuante binário. O banco não sabe sozinho qual arredondamento sua empresa exige.

Por que usamos MongoDB 8.0.28

Em 22 de agosto de 2026, a documentação apresenta 8.3 como a linha atual, mas o próprio versionamento do MongoDB separa linhas major e minor. Linhas major, como 8.0, têm ciclo de cinco anos e são indicadas quando se quer uma janela previsível e controle manual de atualização. Linhas minor entregam recursos antes e pedem atualizações mais frequentes.

Para um curso que precisa continuar reproduzível, minha escolha é a linha major 8.0 no patch estável mais recente: 8.0.28, publicado em 22 de julho de 2026. Não há release candidate nem recurso preview no caminho principal. O driver usado é o oficial mongodb 7.5.0. Isso não declara que 8.3 seja instável; declara apenas que previsibilidade é mais importante para este laboratório.

Suba um MongoDB descartável com Docker

O comando abaixo publica a porta padrão somente para o laboratório local. Ele não configura senha, persistência nem réplica; portanto, não é uma receita de produção. Antes de executar, confirme que você não possui outro serviço usando a porta 27017 nem um container chamado mongo-devclub.

bash
docker run -d \
  --name mongo-devclub \
  -p 127.0.0.1:27017:27017 \
  mongo:8.0.28

docker exec mongo-devclub \
  mongosh --quiet --eval 'db.adminCommand({ ping: 1 })'
{ ok: 1 }

O mongod é o servidor. mongosh é um cliente de terminal executado dentro do container. O retorno ok: 1 prova que o cliente conseguiu mandar um comando ao servidor; ele não prova que sua aplicação Node está usando a mesma URI.

Mantenha o container ativo durante os próximos exemplos; o comando de remoção fica no fim do guia. Se você quiser dados duráveis, autenticação, backup e alta disponibilidade, planeje essas partes separadamente ou use um serviço gerenciado. Um container sem volume perde seu banco quando é removido — o que aqui é proteção contra acidente, não defeito.

O primeiro acesso com o driver oficial do Node.js

Crie uma pasta vazia, inicialize o projeto e instale o driver. Mongoose não entra neste começo porque queremos enxergar a API que conversa diretamente com o MongoDB:

bash
mkdir livraria-mongo
cd livraria-mongo
npm init -y
npm install mongodb@7.5.0
added 12 packages

Crie index.mjs. MongoClient representa o cliente; a URI aponta para o servidor; db() escolhe o database. O comando ping força uma ida ao banco e evita confundir “objeto do cliente criado” com “conexão comprovada”.

js
import { MongoClient } from "mongodb";

const uri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017";
const client = new MongoClient(uri);

try {
  await client.connect();
  const db = client.db("livraria");
  const resposta = await db.command({ ping: 1 });
  console.log({ conexao: resposta.ok === 1 ? "ok" : "falhou" });
} finally {
  await client.close();
}
{ conexao: 'ok' }

finally fecha o cliente até quando uma operação falha. Numa API de longa duração, a prática comum é criar um MongoClient reutilizável durante a vida do processo, em vez de abrir e fechar uma conexão para cada requisição. Neste script curto, fechar no final deixa a execução previsível.

Crie, leia e altere um livro

Agora o banco começa a assumir uma responsabilidade real. collection() seleciona livros; insertOne() cria um documento. Se a coleção ainda não existir, a primeira inserção pode criá-la implicitamente.

js
const livros = client.db("livraria").collection("livros");

const resultado = await livros.insertOne({
  _id: "livro-1",
  titulo: "JavaScript do zero",
  preco: 39.9,
  estoque: 8,
  categorias: ["programação", "javascript"],
});

console.log({
  acknowledged: resultado.acknowledged,
  insertedId: resultado.insertedId,
});
{ acknowledged: true, insertedId: 'livro-1' }

acknowledged: true diz que o servidor confirmou a escrita segundo a configuração usada. Não significa que toda regra de negócio imaginável foi validada; neste ponto, por exemplo, ainda não criamos uma regra contra estoque negativo.

Para ler somente os campos de que a tela precisa, use filtro e projeção:

js
const livro = await livros.findOne(
  { _id: "livro-1" },
  { projection: { _id: 0, titulo: 1, preco: 1, estoque: 1 } },
);

console.log(livro);
{ titulo: 'JavaScript do zero', preco: 39.9, estoque: 8 }

O primeiro objeto é o filtro: qual documento pode entrar no resultado. O segundo contém a projeção: quais campos voltam. _id aparece por padrão, então _id: 0 o retira dessa resposta.

Uma venda de duas unidades pode ser expressa com condição e operador atômico:

js
const alteracao = await livros.updateOne(
  { _id: "livro-1", estoque: { $gte: 2 } },
  { $inc: { estoque: -2 } },
);

console.log({
  matchedCount: alteracao.matchedCount,
  modifiedCount: alteracao.modifiedCount,
});
console.log(await livros.findOne(
  { _id: "livro-1" },
  { projection: { _id: 0, estoque: 1 } },
));
{ matchedCount: 1, modifiedCount: 1 } { estoque: 6 }

$gte significa “maior ou igual”; $inc incrementa o valor pelo número informado, que pode ser negativo. A condição e a alteração acontecem na mesma operação sobre o documento. Isso é mais seguro do que ler 8 no Node, calcular 6 e depois gravar: entre a leitura e a gravação, outra compra poderia mudar o estoque.

A próxima lição abre esses conceitos com calma em documentos e coleções. Depois, o CRUD com Node.js reúne create, read, update e delete num único script.

Flexível não quer dizer sem regra

Por padrão, dois documentos na mesma coleção podem ter campos diferentes. Isso ajuda catálogos variados: livro físico pode ter peso; e-book pode ter formatoArquivo. Entretanto, certas regras não são opcionais. Um pedido sem itens ou um estoque negativo não vira aceitável só porque BSON consegue representá-lo.

MongoDB permite validação com $jsonSchema. A coleção abaixo exige título, preço e estoque, aceita os tipos numéricos BSON usados no exemplo e recusa estoque menor que zero:

js
await db.createCollection("livros_validados", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["titulo", "preco", "estoque"],
      properties: {
        titulo: { bsonType: "string" },
        preco: {
          bsonType: ["double", "int", "long", "decimal"],
          minimum: 0,
        },
        estoque: { bsonType: "int", minimum: 0 },
      },
    },
  },
});
Coleção livros_validados criada com validação ativa.

Ao tentar inserir estoque: -1, o servidor devolve o código 121:

js
try {
  await db.collection("livros_validados").insertOne({
    titulo: "Livro inválido",
    preco: 20,
    estoque: -1,
  });
} catch (erro) {
  console.log({ codigo: erro.code, motivo: "estoque abaixo de zero" });
}
{ codigo: 121, motivo: 'estoque abaixo de zero' }

A validação no banco protege qualquer cliente, não apenas uma rota da API. Ela não elimina a validação de entrada: a API ainda deve devolver uma mensagem boa ao usuário e impedir trabalho desnecessário. As duas camadas resolvem problemas diferentes.

Incorporar ou referenciar é a decisão central

Incorporar é colocar o dado relacionado dentro do documento. Referenciar é guardar um identificador que aponta para outro documento. Não existe regra “MongoDB sempre incorpora”. Existe pergunta sobre acesso, crescimento e consistência.

No pedido, incorporar o endereço de entrega preserva o histórico e permite ler a confirmação de uma vez. Referenciar o cliente evita repetir todo o cadastro em cada compra. Incorporar milhares de avaliações dentro de um livro pode fazer o documento crescer sem limite; manter avaliações em outra coleção pode ser mais adequado.

Minha regra prática é começar pela tela ou operação mais importante: quais dados ela lê e altera junto? Depois confronto esse desenho com crescimento, duplicação e necessidade de atualização. A lição de modelagem MongoDB faz essa decisão em um pedido completo e mostra onde uma transação entra.

Aggregation pipeline é uma linha de preparo

Quando a pergunta deixa de ser “qual é este livro?” e vira “quais livros mais faturaram?”, uma busca simples não basta. O aggregation pipeline passa documentos por estágios. Um estágio filtra, outro abre arrays, outro agrupa e o seguinte ordena.

Pense na bancada de expedição: a primeira pessoa separa somente pedidos pagos; a segunda abre cada caixa e coloca os itens na esteira; a terceira reúne itens do mesmo título; a quarta ordena pelo valor vendido. Cada etapa recebe o resultado da anterior. O limite: o banco pode otimizar partes da execução, usar memória, disco e índices; não existe uma pessoa nem uma caixa material em cada estágio.

js
const ranking = await pedidos.aggregate([
  { $match: { status: "pago" } },
  { $unwind: "$itens" },
  {
    $group: {
      _id: "$itens.titulo",
      unidades: { $sum: "$itens.quantidade" },
    },
  },
  { $sort: { unidades: -1 } },
]).toArray();

console.log(ranking);
[ { _id: 'JavaScript do zero', unidades: 4 }, { _id: 'Node.js na prática', unidades: 3 }, { _id: 'CSS sem sustos', unidades: 2 } ]

aggregate() devolve cursor; toArray() consome esse cursor e materializa os resultados. Em bases grandes, não transforme qualquer consulta em array sem pensar em limite e memória. A lição de aggregation pipeline constrói o relatório estágio por estágio e reproduz uma falha de expressão.

Índice é um atalho que também cobra manutenção

Sem um índice adequado, MongoDB pode percorrer todos os documentos para atender um filtro. Um índice mantém valores de campos em uma estrutura ordenada para reduzir o espaço de busca. No catálogo, é como um índice alfabético que aponta para as fichas: procurar “Node.js” deixa de exigir abrir cada pasta.

O limite dessa imagem é o custo. Um índice real ocupa disco e memória, e cada escrita precisa atualizá-lo. Criar um índice para todo campo deixa inserções mais caras e não garante que o planejador vá escolhê-los. Índice nasce de consulta frequente observada, e explain("executionStats") mostra o que realmente foi examinado.

No laboratório de 80 mil pedidos, a mesma consulta examinou 80 mil documentos com COLLSCAN. Depois do índice composto, examinou 20 chaves, zero documentos e usou IXSCAN, porque a projeção estava coberta. A medição completa e seus limites estão em índices no MongoDB.

MongoDB ou PostgreSQL: a escolha que eu faria

“NoSQL ou SQL?” é amplo demais. Eu começo pela forma como o domínio mantém consistência e pelas consultas mais valiosas. Esta tabela é uma decisão editorial para um projeto novo, não uma lei sobre produtos:

Situação observada Minha primeira opção Por quê O que eu validaria antes
agregado lido junto, com campos que variam por subtipo MongoDB documento acompanha o objeto e permite incorporar partes crescimento do documento e consultas cruzadas
muitas relações obrigatórias e relatórios combinando entidades PostgreSQL chaves, constraints e joins expressam o centro do domínio volume, formato dos relatórios e competência do time
pedido precisa preservar endereço e preço da compra MongoDB com snapshot incorporado histórico sai numa leitura e muda como unidade duplicação aceitável e limite do documento
financeiro com regras relacionais densas PostgreSQL integridade relacional e SQL tornam regras explícitas necessidade real de escala e transações concorrentes
catálogo heterogêneo, como livros, cursos e kits MongoDB documentos podem ter atributos próprios por tipo validação mínima comum e estratégia de índices
equipe domina profundamente uma das duas opções a opção dominada, se atender os requisitos operação conhecida costuma valer mais que novidade requisitos que de fato forçariam migração

Minha assinatura aqui é simples: não escolho MongoDB para evitar aprender modelagem. Eu o escolho quando a fronteira de documento explica o domínio e reduz trabalho sem esconder uma rede de relações que o produto realmente precisa consultar. Se o problema é naturalmente relacional, o guia de PostgreSQL oferece uma comparação concreta.

O mapa do cluster MongoDB

Estude as lições na ordem. Cada uma usa a mesma Livraria Horizonte, mas recria seu database para que você possa repetir a missão:

Ordem Lição Resultado verificável
11 Documentos e coleções criar coleção, consultar campo aninhado e provocar validação 121
12 CRUD com Node.js inserir, filtrar, alterar e remover com o driver oficial
13 Modelagem MongoDB escolher incorporação e referência e confirmar uma transação
14 Aggregation pipeline produzir ranking de vendas e juntar clientes com $lookup
15 Índices MongoDB medir COLLSCAN e IXSCAN sobre a mesma consulta

Os números começam em 11 porque a trilha de banco de dados já ensina primeiro os fundamentos relacionais. Você não precisa “esquecer SQL” para aprender documentos; comparar os modelos torna cada decisão mais consciente.

Depois das cinco lições, o tutorial API REST com MongoDB, validação e testes reúne o driver oficial, Express, schema da coleção, índice único, paginação e aggregation numa API completa executada em Docker.

O laboratório comprova mecanismo, não operação de produção

Rodar os exemplos localmente prova que o servidor 8.0.28 aceitou os comandos, que o driver 7.5.0 converteu os valores e que as saídas mostradas correspondem àquele estado. Isso é uma evidência importante, mas tem uma fronteira: o container de um nó não prova disponibilidade, recuperação de desastre, capacidade, segurança nem comportamento sob concorrência real.

Antes de levar a livraria ao ar, transforme requisitos operacionais em testes. Defina quem pode conectar e o que cada usuário pode fazer; habilite autenticação e transporte seguro; planeje backup e restauração; escolha uma topologia com redundância; observe conexões, latência, espaço e consultas lentas. Um backup só vira proteção depois que uma restauração foi ensaiada.

Também não copie os 80 mil pedidos sintéticos da aula de índices como previsão de produção. Eles foram desenhados para tornar uma diferença visível e repetível. Seus dados terão outras proporções de status, clientes mais ativos, documentos maiores e períodos de pico. Capture os filtros e ordenações que a aplicação realmente envia, use explain num ambiente seguro e acompanhe o custo das escritas depois de adicionar índices.

Essa separação evita duas conclusões perigosas: “funcionou no Docker, então está pronto” e “o banco é complexo demais para começar”. O laboratório ensina os controles; a produção acrescenta responsabilidades mensuráveis. Você começa pequeno sem fingir que pequeno e definitivo são a mesma coisa.

Missão: prove que o banco mudou, não apenas que o script rodou

Com o container ativo e index.mjs conectado, faça três mudanças: cadastre livro-2 com estoque 5, retire duas unidades usando filtro com $gte e consulte somente titulo e estoque. Sua evidência de conclusão é esta sequência:

text
insertedId: livro-2
matchedCount: 1
modifiedCount: 1
documento final: { titulo: 'Node.js na prática', estoque: 3 }
Missão concluída quando a consulta final mostra estoque 3 sem o campo _id.

Depois altere a quantidade solicitada para 99. O resultado correto é matchedCount: 0, e o estoque deve continuar 3. Essa segunda tentativa é a parte importante: ela comprova que o filtro representa uma regra, e não apenas um caminho feliz decorado.

Com a missão conferida, encerre o recurso local. Este comando remove somente o container chamado mongo-devclub criado no começo do guia:

bash
docker rm -f mongo-devclub
mongo-devclub

Trilha

Banco de dados

SQL, PostgreSQL, MongoDB e Prisma: modelar, consultar e conectar o banco na API que você escreveu.

Ver a trilha
  1. 01Documentos e coleções no MongoDB: estrutura e consultas
  2. 02CRUD no MongoDB com Node.js e o driver oficial
  3. 03Modelagem MongoDB: quando incorporar ou referenciar dados
  4. 04Aggregation pipeline no MongoDB: relatório passo a passo
  5. 05Índices no MongoDB: medir COLLSCAN e IXSCAN

node.js

Perguntas frequentes

MongoDB é um banco sem estrutura?
Não. Ele tem estrutura de documentos e tipos BSON, mas permite que documentos da mesma coleção tenham campos diferentes. Você também pode aplicar validação de schema quando uma regra precisa ser obrigatória.
MongoDB substitui PostgreSQL em qualquer projeto?
Não. Os dois resolvem persistência com modelos diferentes. MongoDB costuma funcionar bem quando agregados cabem naturalmente em documentos; PostgreSQL costuma ser uma escolha forte para relações e regras cruzadas.
Preciso usar Mongoose para acessar MongoDB no Node.js?
Não. O driver oficial mongodb conecta, consulta e altera dados diretamente. Mongoose adiciona uma camada de modelos e validação, útil em alguns times, mas não é requisito nem substitui entender o banco.
Qual versão do MongoDB este guia usa?
Os exemplos foram executados no MongoDB 8.0.28, patch estável da linha major 8.0 em 22 de agosto de 2026, com o driver oficial Node.js 7.5.0.
Posso guardar preço como Number no MongoDB?
Pode em um laboratório, como neste guia. Para dinheiro em produção, escolha conscientemente centavos inteiros ou Decimal128 e defina regras de arredondamento; ponto flutuante binário não representa todo decimal exato.

O código deste guia foi executado em MongoDB 8.0.28, MongoDB Node.js Driver 7.5.0, Node 26.3.0 e Docker 29.5.3, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MongoDB Manual — Introduction to MongoDB — mongodb.com
  2. MongoDB Manual — MongoDB Versioning — mongodb.com
  3. MongoDB Manual — Release Notes 8.0 — mongodb.com
  4. MongoDB Manual — CRUD Operations — mongodb.com
  5. MongoDB Node.js Driver — mongodb.com