MongoDB Academy · Lección

Leer documentos con el controlador de Node.js

Conectará un script de Node.js a MongoDB y realizará operaciones de inserción y búsqueda mediante el controlador oficial.

Lección 4 de 413 pasos

Leer documentos con el controlador de Node.js es una lección gratuita de MongoDB Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de MongoDB Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de MongoDB Academy incluye 4 lecciones en total.

El controlador oficial de Node.js

El controlador de MongoDB para Node.js (el paquete npm mongodb) es el cliente oficial de bajo nivel para conectarse a MongoDB desde Node.js. MongoDB Inc. se encarga de su mantenimiento, es compatible con toda la API de MongoDB e incluye definiciones de tipos de TypeScript.

El controlador expone una clase MongoClient que administra un grupo de conexiones: un conjunto de conexiones TCP preestablecidas que se reutilizan entre solicitudes. Esto elimina el coste de crear una conexión nueva para cada operación. La mayoría de las aplicaciones de Node.js en producción comparten una única instancia de MongoClient durante toda la vida del proceso.

# Install the MongoDB Node.js driver
npm install mongodb

# TypeScript types are included - no @types/mongodb needed

Crear un MongoClient y conectarse

Cree un MongoClient con el URI de la cadena de conexión y llame a connect(). La conexión no se establece hasta ejecutar connect() (o la primera operación). Una vez conectado, recupere una referencia a la base de datos con client.db('dbName') y una referencia a una colección con db.collection('collName').

Práctica recomendada: cree el MongoClient una sola vez al iniciar la aplicación y expórtelo o inyéctelo. NO cree un cliente nuevo para cada solicitud: esto agota los descriptores de archivo y anula las ventajas del grupo de conexiones.

const { MongoClient } = require('mongodb');

const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
const client = new MongoClient(uri);

async function main() {
  await client.connect();
  console.log('Connected to MongoDB');

  const db = client.db('myapp');
  const users = db.collection('users');

  // ... perform operations ...

  await client.close();
}

main().catch(console.error);

Grupo de conexiones y opciones

MongoClient mantiene un grupo de conexiones TCP abiertas. Cuando una operación debe ejecutarse, toma prestada una conexión del grupo, ejecuta la operación y devuelve la conexión. Opciones principales del grupo:

  • maxPoolSize (valor predeterminado: 100): número máximo de conexiones simultáneas
  • minPoolSize (valor predeterminado: 0): conexiones que se mantienen activas cuando no hay actividad
  • connectTimeoutMS: tiempo de espera para la conexión inicial
  • serverSelectionTimeoutMS: tiempo de espera si no se puede acceder a ningún servidor

Para la mayoría de los servidores web de Node.js, el tamaño predeterminado del grupo, 100, es adecuado. Las funciones sin servidor (AWS Lambda) deberían utilizar grupos más pequeños.

const client = new MongoClient(uri, {
  maxPoolSize: 20,           // Max 20 simultaneous connections
  minPoolSize: 5,            // Keep 5 alive when idle
  connectTimeoutMS: 5000,    // 5s to establish initial connection
  serverSelectionTimeoutMS: 5000  // 5s to select a server
});

// The client pool is shared across all operations
// Never create per-request clients!

insertOne e insertMany en Node.js

Todas las operaciones del controlador devuelven Promises. Utilice async/await para escribir código claro y fácil de leer. Los métodos de inserción aceptan documentos como objetos simples de JavaScript; el controlador los serializa automáticamente a BSON.

Los objetos de resultado incluyen acknowledged (booleano) y insertedId/insertedIds. Los usuarios de TypeScript pueden pasar un parámetro de tipo genérico para obtener documentos tipados: db.collection<User>('users').

const db = client.db('shop');
const products = db.collection('products');

// insertOne
const { insertedId } = await products.insertOne({
  name: 'Wireless Mouse',
  price: 29.99,
  stock: 150,
  createdAt: new Date()
});
console.log('Inserted product:', insertedId);

// insertMany
const { insertedCount } = await products.insertMany([
  { name: 'Keyboard', price: 49.99, stock: 80 },
  { name: 'Monitor',  price: 299.99, stock: 25 }
]);
console.log('Inserted:', insertedCount);

findOne en Node.js

collection.findOne(filter, options) devuelve una Promise que se resuelve en el documento coincidente o en null. El objeto opcional options acepta projection, sort y maxTimeMS.

Un patrón habitual en las API web consiste en buscar un recurso mediante su parámetro de URL (la cadena id), convertirlo en un ObjectId para la consulta y devolver un 404 si se obtiene null. Valide siempre que la cadena id tenga un formato válido de ObjectId antes de construir uno; las cadenas no válidas producen una excepción síncrona.

const { ObjectId } = require('mongodb');

// Express route handler example
app.get('/products/:id', async (req, res) => {
  let objId;
  try {
    objId = new ObjectId(req.params.id);
  } catch {
    return res.status(400).json({ error: 'Invalid product id' });
  }

  const product = await db.collection('products').findOne(
    { _id: objId },
    { projection: { name: 1, price: 1, stock: 1, _id: 0 } }
  );

  if (!product) return res.status(404).json({ error: 'Not found' });
  res.json(product);
});

find() y Cursor en Node.js

En Node.js, collection.find(filter, options) devuelve un FindCursor. Puede iterarlo con for await...of, llamar a .toArray() para cargar todos los resultados o utilizar .forEach(callback). Todas las opciones (sort, limit, skip y projection) se pueden pasar en el objeto de opciones o encadenar como métodos.

Para los endpoints de API que devuelven listas, .toArray() resulta práctico; asegúrese de aplicar siempre .limit(n) para evitar cargar conjuntos de resultados sin límite. Para trabajos de procesamiento de datos (exportaciones y migraciones), utilice for await...of para procesar los datos en flujo.

// List endpoint with pagination
app.get('/products', async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = 20;
  const skip = (page - 1) * limit;

  const products = await db.collection('products')
    .find({ stock: { $gt: 0 } })
    .sort({ price: 1 })
    .skip(skip)
    .limit(limit)
    .project({ name: 1, price: 1, _id: 1 })
    .toArray();

  res.json({ page, products });
});

Patrones de gestión de errores

Todas las operaciones del controlador pueden producir errores: problemas de red, fallos de autenticación, conflictos de escritura y errores de validación. Utilice try/catch alrededor de todas las llamadas a la base de datos en el código de producción. Tipos de error principales:

  • MongoNetworkError: problema de conectividad; la lógica de reintento puede ser útil
  • MongoServerError con código 11000: infracción por clave duplicada
  • MongoServerError con código 121: el documento no superó la validación del esquema

Implemente un contenedor withRetry para los errores de red transitorios. No reintente los errores de validación ni los de clave duplicada: indican un error lógico, no un fallo transitorio.

async function createUser(data) {
  try {
    const result = await db.collection('users').insertOne(data);
    return { id: result.insertedId };
  } catch (err) {
    if (err.code === 11000) {
      // Duplicate email
      throw new Error('EMAIL_IN_USE');
    }
    if (err.code === 121) {
      // Schema validation failed
      throw new Error('INVALID_DATA');
    }
    // Network or other error - let it propagate
    throw err;
  }
}

Gestionar ObjectId en las respuestas de API

Al devolver documentos de MongoDB en la respuesta de una API REST, los objetos ObjectId se serializan como una representación de cadena mediante JSON.stringify y aparecen como '64a2f3b1c9e7e12345678901'. A continuación, los clientes envían esta cadena como identificador en las solicitudes posteriores.

Un patrón habitual consiste en transformar los documentos en una función de mapeo: convertir _id en id como cadena y eliminar el prefijo de guion bajo específico de MongoDB, que puede resultar confuso para los clientes. Esto también evita exponer detalles internos de implementación de MongoDB a los consumidores de la API.

// Transform MongoDB document for API response
function toPublicUser(doc) {
  const { _id, passwordHash, ...rest } = doc;
  return {
    id: _id.toString(),  // ObjectId -> string
    ...rest              // All other fields, minus passwordHash
  };
}

const user = await db.collection('users').findOne({ email });
if (user) res.json(toPublicUser(user));

// Client sends 'id' string back:
// GET /users/64a2f3b1c9e7e12345678901
// Server converts: new ObjectId(req.params.id)

Compartir el cliente entre módulos

El patrón recomendado en Node.js consiste en inicializar MongoClient una sola vez y compartirlo entre módulos mediante un patrón singleton o inyección de dependencias. Un enfoque habitual es utilizar un módulo db.js que exporte una función connectDB() y un accesor getDB().

Llame a connectDB() una vez al iniciar la aplicación (en server.js o app.js). Todos los controladores de rutas y módulos de servicio llaman a getDB() para obtener la referencia a la base de datos sin crear conexiones nuevas.

// db.js - singleton pattern
const { MongoClient } = require('mongodb');
let db;

async function connectDB() {
  const client = new MongoClient(process.env.MONGODB_URI);
  await client.connect();
  db = client.db('myapp');
  console.log('MongoDB connected');
}

function getDB() {
  if (!db) throw new Error('DB not initialized - call connectDB() first');
  return db;
}

module.exports = { connectDB, getDB };

// Usage in a route:
// const { getDB } = require('./db');
// const db = getDB();
// await db.collection('users').find({}).toArray();

Cierre ordenado

Cuando el proceso de Node.js recibe una señal de cierre (SIGTERM, SIGINT), cierre MongoClient de forma ordenada mediante client.close(). Esto vacía los búferes de escritura pendientes, cierra los cursores abiertos y libera correctamente las conexiones TCP.

Sin un cierre ordenado, MongoDB puede detectar una desconexión abrupta, activar la gestión de errores en las operaciones en curso y mantener abierto el grupo de conexiones del servidor hasta que se active el tiempo de espera de inactividad. En sistemas con mucho tráfico, numerosas desconexiones abruptas pueden agotar el límite de conexiones de MongoDB.

// Graceful shutdown handlers
const client = new MongoClient(uri);
await client.connect();

process.on('SIGINT', async () => {
  console.log('Shutting down...');
  await client.close();
  process.exit(0);
});

process.on('SIGTERM', async () => {
  console.log('Received SIGTERM');
  await client.close();
  process.exit(0);
});

// Express:
const server = app.listen(3000);
process.on('SIGTERM', () => server.close(async () => {
  await client.close();
}));

Integración con TypeScript

El controlador de MongoDB para Node.js incluye compatibilidad de primera clase con TypeScript. Puede definir una interfaz para la estructura de su documento y pasarla como genérico a db.collection<MyType>(). De este modo, el controlador proporciona resultados con seguridad de tipos para findOne, find().toArray() y mucho más.

Hay un detalle importante: el tipo de TypeScript debe incluir _id?: ObjectId para coincidir con lo que devuelve MongoDB. Al excluir _id mediante una proyección, TypeScript lo sigue incluyendo en el tipo; puede usar WithId<T> o definir tipos de entrada y salida separados para lograr una implementación totalmente segura en cuanto a tipos.

import { MongoClient, ObjectId } from 'mongodb';

// Define your document interface
interface User {
  _id?: ObjectId;
  name: string;
  email: string;
  age: number;
  createdAt: Date;
}

const users = db.collection<User>('users');

// Fully typed result
const user = await users.findOne({ email: 'alice@test.com' });
// user is User | null
if (user) console.log(user.name.toUpperCase()); // TypeScript knows name is string

Comprobación rápida

Compruebe su comprensión de los conceptos de MongoDB y las bases de datos NoSQL de esta lección.

Resumen de la lección

En esta lección ha aprendido que MongoClient administra un conjunto de conexiones: debe crear una única instancia al iniciar la aplicación y compartirla entre todos los módulos para evitar el coste adicional de las conexiones; todas las operaciones devuelven Promises y deben esperarse con try/catch para gestionar correctamente los errores, donde el código de error 11000 indica infracciones por claves duplicadas; y los genéricos de TypeScript en collection<T>() proporcionan acceso a documentos con seguridad de tipos y compatibilidad completa con el IDE. A continuación, exploraremos el conjunto completo de operadores de consulta de comparación y lógicos de MongoDB para escribir filtros precisos y complejos.

Gratis para empezar

Aprende JavaScript con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
30
Lecciones
120

Preguntas frecuentes

¿La lección «Leer documentos con el controlador de Node.js» es gratis?

Sí — el texto completo de «Leer documentos con el controlador de Node.js» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de MongoDB Academy, actualiza a CoddyKit PRO. El curso de MongoDB Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Leer documentos con el controlador de Node.js»?

Conectará un script de Node.js a MongoDB y realizará operaciones de inserción y búsqueda mediante el controlador oficial. Practicas MongoDB Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar MongoDB Academy?

No se requiere experiencia previa. MongoDB Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Leer documentos con el controlador de Node.js»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de MongoDB Academy?

Sí. Cada lección de MongoDB Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. insertOne e insertMany
  2. findOne frente a find: explicación de los cursores
  3. Consultar campos anidados y arrays
  4. Leer documentos con el controlador de Node.js
← Volver a MongoDB Academy