Overview
La mayoría de las aplicaciones usan una sola conexión a MongoDB. Sin embargo, algunas aplicaciones se benefician de múltiples conexiones para la separación de datos, optimización del rendimiento o requisitos arquitectónicos.
En este tutorial, aprenderás cómo establecer múltiples conexiones a MongoDB dentro de una sola aplicación Node.js.
Casos de uso para múltiples conexiones de MongoDB
Las conexiones múltiples de MongoDB son útiles en aplicaciones que requieren separación de datos, rendimiento mejorado o flexibilidad arquitectónica.
Los casos de uso comunes para múltiples conexiones MongoDB incluyen los siguientes:
Aplicaciones multiinquilino: Cada inquilino utiliza una base de datos separada con su propia conexión para el aislamiento y la seguridad de los datos.
Arquitectura de microservicios: cada microservicio mantiene su propia conexión dedicada a la base de datos
Distribución de carga: distribuye operaciones de lectura y guardar entre varias instancias de la base de datos.
Procesamiento de datos: Recuperar datos de múltiples servidores MongoDB simultáneamente para análisis o reporte
Aplicaciones multiinquilino
Considera una aplicación multitenant en la que diferentes inquilinos o clientes comparten la misma aplicación web pero requieren bases de datos separadas e independientes. En este escenario, cada inquilino puede tener su propia conexión MongoDB dedicada. Esto garantiza la separación de datos, la seguridad y la personalización para cada inquilino, mientras todos operan dentro de la misma aplicación. Este enfoque simplifica la gestión y proporciona una forma de escalar a medida que nuevos inquilinos se unen a la plataforma sin afectar a los existentes.
Ten en cuenta los siguientes conceptos de MongoDB para implementar múltiples conexiones en tu aplicación:
Estos conceptos desempeñan un papel importante en escenarios donde se requieren múltiples conexiones a MongoDB para una gestión eficiente de datos y optimización del rendimiento.
Tutorial
En este tutorial, realizarás las siguientes acciones:
Configura tu entorno e instala las dependencias necesarias
Conéctese a MongoDB
Configura la conexión principal de MongoDB
Configura conexiones secundarias de MongoDB
Utilice un esquema existente
Establece la flexibilidad del esquema
Cambiar bases de datos dentro de la misma conexión
Requisitos previos
Antes de comenzar este tutorial, debe tener un clúster de MongoDB Atlas. Para aprender cómo crear un clúster gratuito de MongoDB Atlas, consulta el tutorial MongoDB Comenzando.
Nota
Este tutorial utiliza MongoDB Server 8.0.12 y Node.js versión 20.15.1.
Instale las dependencias
Instala las dependencias necesarias para este tutorial:
npm install express mongoose
Este tutorial utiliza Express.js como framework web y Mongoose para el modelado de objetos en MongoDB.
Nota
Este tutorial utiliza Mongoose, una popular biblioteca de Modelado de Objeto de Datos (ODM) para MongoDB. Para obtener más información sobre cómo comenzar con Mongoose, consulta la Comienza con Mongoose tutorial.
Configurar variables de entorno
Crear un(a) .env archivo en el directorio raíz de su proyecto para almacenar sus cadenas de conexión MongoDB:
touch .env
Agrega las siguientes variables de entorno a tu archivo .env:
:orphan: PRIMARY_CONN_STR=<your-primary-connection-string> SECONDARY_CONN_STR=<your-secondary-connection-string>
Reemplace los valores de marcador de posición con sus cadenas de conexión reales de MongoDB Atlas. Para aprender a encontrar tu cadena de conexión de MongoDB Atlas, consulta el tutorial Conéctate a tu clúster.
Instale el paquete dotenv para cargar tus variables de entorno:
npm install dotenv
Ahora tienes un proyecto listo para configurar múltiples conexiones a MongoDB en tu aplicación.
Estructura del proyecto
Cuando complete este tutorial, su directorio de proyecto para esta aplicación tendrá la siguiente estructura:
mongodb-multiple-connections/ ├── .env # Environment variables ├── package.json # Project dependencies ├── package-lock.json # Dependency lock file ├── node_modules/ # Installed packages ├── index.js # Main application file ├── db.primary.js # Primary connection configuration ├── db.secondary.js # Secondary connection configuration └── product.schema.js # Product schema definition
Los archivos clave que crearás en este tutorial sirven los siguientes propósitos:
index.js: Su archivo de aplicación principal que importa y usa ambas conexiones
db.primary.js: Configura la conexión principal de MongoDB utilizando
mongoose.connect()db.secundario.js: Configura las conexiones secundarias a MongoDB utilizando
mongoose.createConnection()product.esquema.js: Define el esquema de producto reutilizable para ambas conexiones.
.env: Almacena de forma segura tus cadenas de conexión de MongoDB
Configurar la conexión principal de MongoDB
En esta sección, aprende cómo configurar la conexión primaria de MongoDB en tu aplicación usando Mongoose. Usa el mongoose.connect() el método para establecer la conexión primaria de la base de datos MongoDB para tu aplicación. Este método gestiona un único grupo de conexiones para toda la aplicación.
Crear el archivo de conexión principal
En un nuevo archivo llamado db.primary.js, defina un método de conexión para usar en el archivo principal de su aplicación, index.js. Este método configura la conexión de MongoDB y gestiona los eventos. Copie y pegue el siguiente código en el archivo db.primary.js:
require('dotenv').config(); const mongoose = require("mongoose"); module.exports = async (uri, options = {}) => { // By default, Mongoose skips properties not defined in the schema (strictQuery). // You can adjust it based on your configuration. mongoose.set('strictQuery', true); // Connect to MongoDB try { await mongoose.connect(uri, options); console.info("MongoDB primary connection initiated"); } catch (err) { console.error("MongoDB primary connection failed, " + err); } // Event handling mongoose.connection.once('open', () => console.info("MongoDB primary connection opened!")); mongoose.connection.on('connected', () => console.info("MongoDB primary connection succeeded!")); mongoose.connection.on('error', (err) => { console.error("MongoDB primary connection failed, " + err); mongoose.disconnect(); }); mongoose.connection.on('disconnected', () => console.info("MongoDB primary connection disconnected!")); // Graceful exit process.on('SIGINT', async () => { try { await mongoose.connection.close(); console.info("Mongoose primary connection disconnected through app termination!"); process.exit(0); } catch (err) { console.error("Error during graceful shutdown:", err); process.exit(1); } }); }
Crear el esquema del producto
Cree esquemas para realizar operaciones en su aplicación. Guarde el esquema en un archivo separado llamado product.schema.js y expórtelo, como se muestra en el siguiente código:
const mongoose = require("mongoose"); module.exports = (options = {}) => { // Schema for Product return new mongoose.Schema( { store: { _id: mongoose.Types.ObjectId, // Reference-id to the store collection name: String }, name: String, price: Number, category: String, description: String // add required properties }, options ); }
Usa la conexión primaria
Importa el archivo db.primary.js en tu archivo principal index.js y usa el método definido allí para establecer la conexión principal de MongoDB. También puedes pasar un objeto de opciones de conexión opcional si es necesario.
Luego, importa el archivo product.schema.js para acceder al esquema del producto. Esto te permite crear un modelo y realizar operaciones relacionadas con productos en tu aplicación. Copia y pega el siguiente código en tu archivo index.js para configurar la conexión principal y ejecutar operaciones en datos de productos de muestra:
// Load environment variables require('dotenv').config(); const mongoose = require("mongoose"); // Async function to establish the primary MongoDB connection async function establishPrimaryConnection() { try { await require("./db.primary.js")(process.env.PRIMARY_CONN_STR, { // (optional) connection options }); } catch (error) { console.error('Failed to establish primary connection:', error); process.exit(1); } } // Initialize connection establishPrimaryConnection(); // Import Product Schema const productSchema = require("./product.schema.js")({ collection: "products", // Pass configuration options if needed }); // Create Model const ProductModel = mongoose.model("Product", productSchema); // Sample products data const sampleProducts = [ { name: "Laptop Pro", price: 1299.99, category: "Electronics", description: "High-performance laptop for professionals" }, { name: "Wireless Headphones", price: 199.99, category: "Electronics", description: "Premium noise-cancelling headphones" }, { name: "Coffee Maker", price: 89.99, category: "Kitchen", description: "Automatic drip coffee maker" } ]; // Wait for connection to be ready before executing operations mongoose.connection.once('open', async () => { console.log('Primary database connected, executing operations...'); try { // Check if products already exist const existingCount = await ProductModel.countDocuments(); console.log(`Existing products in primary DB: ${existingCount}`); if (existingCount === 0) { console.log('Inserting sample products into primary database...'); await ProductModel.insertMany(sampleProducts); console.log('Sample products inserted into primary database!'); } // Find and display a product let product = await ProductModel.findOne(); console.log('Product found in primary DB:', product); // Display all products const allProducts = await ProductModel.find(); console.log(`Total products in primary DB: ${allProducts.length}`); } catch (error) { console.error('Error with primary database operations:', error); } });
Primary database connected, executing operations... MongoDB primary connection initiated Existing products in primary DB: 3 Product found in primary DB: { _id: new ObjectId('...'), name: 'Laptop Pro', __v: 0 } Total products in primary DB: 3
donde su aplicación requiere múltiples conexiones de MongoDB.
Configurar conexiones secundarias de MongoDB
Puedes configurar conexiones secundarias de MongoDB para diversos casos de uso. En esta sección, aprenderás a configurar y usar la conexión secundaria.
Crea el Archivo de Conexión Secundaria
Crea un código de conexión en un archivo db.secondary.js utilizando mongoose.createConnection() el método. Este método te permite establecer pools de conexiones separados, cada uno adaptado a un caso de uso específico o un patrón de acceso a los datos, a diferencia del método mongoose.connect() que utilizaste anteriormente para la conexión primaria a MongoDB:
const mongoose = require("mongoose"); module.exports = (uri, options = {}) => { // Connect to MongoDB const db = mongoose.createConnection(uri, options); // By default, Mongoose skips properties not defined in the schema (strictQuery). // Adjust it based on your configuration. db.set('strictQuery', true); // Event handling db.once('open', () => console.info("MongoDB secondary connection opened!")); db.on('connected', () => console.info(`MongoDB secondary connection succeeded!`)); db.on('error', (err) => { console.error(`MongoDB secondary connection failed, ` + err); db.close(); }); db.on('disconnected', () => console.info(`MongoDB secondary connection disconnected!`)); // Graceful exit process.on('SIGINT', async () => { try { await db.close(); console.info(`Mongoose secondary connection disconnected through app termination!`); process.exit(0); } catch (err) { console.error("Error during graceful shutdown:", err); process.exit(1); } }); // Export db object return db; }
Utilice la conexión secundaria
Importe el archivo db.secondary.js en su archivo principal index.js, cree el objeto de conexión con una variable llamada db y utilice el método definido allí para establecer la conexión secundaria de MongoDB. También puede pasar un objeto de opciones de conexión opcional si es necesario. Añada el siguiente código al final del archivo index.js:
// Load environment variables require('dotenv').config(); // Establish the secondary MongoDB connection const db = require("./db.secondary.js")(process.env.SECONDARY_CONN_STR, { // (optional) connection options }); // Import Product Schema const SecondaryProductSchema = require("./product.schema.js")({ collection: "products", // Pass configuration options if needed }); // Create Model using the secondary connection const SecondaryProductModel = db.model("Product", SecondaryProductSchema); // Sample products data for secondary database const SecondarySampleProducts = [ { name: "Smart Watch", price: 199.99, category: "Electronics", description: "Advanced fitness tracking smartwatch" }, { name: "Bluetooth Speaker", price: 79.99, category: "Electronics", description: "Portable wireless speaker with premium sound" }, { name: "Desk Lamp", price: 49.99, category: "Home", description: "LED desk lamp with adjustable brightness" } ]; // Wait for secondary connection to be ready before executing operations db.once('open', async () => { console.log('Secondary database connected, executing operations...'); try { // Check if products already exist const existingCount = await SecondaryProductModel.countDocuments(); console.log(`Existing products in secondary DB: ${existingCount}`); if (existingCount === 0) { console.log('Inserting sample products into secondary database...'); await SecondaryProductModel.insertMany(SecondarySampleProducts); console.log('Sample products inserted into secondary database!'); } // Find and display a product let product = await SecondaryProductModel.findOne(); console.log('Product found in secondary DB:', product); // Display all products const allProducts = await SecondaryProductModel.find(); console.log(`Total products in secondary DB: ${allProducts.length}`); } catch (error) { console.error('Error with secondary database operations:', error); } });
Primary database connected, executing operations... MongoDB primary connection initiated MongoDB secondary connection succeeded! MongoDB secondary connection opened! Secondary database connected, executing operations... Existing products in primary DB: 3 Existing products in secondary DB: 6 Product found in primary DB: { _id: new ObjectId('...'), name: 'Laptop Pro', __v: 0 } Product found in secondary DB: { _id: new ObjectId('...'), name: 'Smart Watch', __v: 0 } Total products in primary DB: 3 Total products in secondary DB: 3
Ahora que ha establecido la conexión, puede usar el nuevo db objeto para crear un modelo. Las siguientes secciones exploran diferentes escenarios y ejemplos para ayudarte a elegir la configuración que mejor se alinee con tus necesidades de acceso a los datos y gestión de datos específicas.
Utilice un esquema existente
Si ambas conexiones operan en el mismo modelo de datos, utilice el mismo archivo product.schema.js que se empleó en la conexión principal.
Importe el archivo product.schema.js para acceder al esquema del producto. Esto le permite crear un modelo usando el objeto db y realizar operaciones relacionadas con los productos en su aplicación:
// Import Product Schema const secondaryProductSchema = require("./product.schema.js")({ collection: "products", // Pass configuration options if needed }); // Create Model const SecondaryProductModel = db.model("Product", secondaryProductSchema); // Wait for secondary connection to be ready before executing operations db.once('open', async () => { console.log('Secondary database connected, executing operations...'); try { // Check if products already exist in secondary database const existingCount = await SecondaryProductModel.countDocuments(); console.log(`Existing products in secondary DB: ${existingCount}`); if (existingCount === 0) { console.log('Inserting sample products into secondary database...'); await SecondaryProductModel.insertMany(sampleProducts); console.log('Sample products inserted into secondary database!'); } // Find and display a product let product = await SecondaryProductModel.findOne(); console.log('Product found in secondary DB:', product); // Display all products const allProducts = await SecondaryProductModel.find(); console.log(`Total products in secondary DB: ${allProducts.length}`); } catch (error) { console.error('Error with secondary database operations:', error); } });
Establecer flexibilidad de esquema
Al trabajar con múltiples conexiones de MongoDB, es esencial tener la flexibilidad para adaptar tu esquema según casos de uso específicos. Aunque la conexión principal puede requerir un esquema estricto con validación para garantizar la integridad de los datos, existen situaciones en las que una conexión secundaria cumple una función diferente. Por instancia, una conexión secundaria puede almacenar datos para análisis en un servidor de ficheros, con requisitos de esquema variables impulsados por casos de uso anteriores. En esta sección, explorarás cómo configurar la flexibilidad del esquema para tu conexión secundaria, lo que te permitirá satisfacer las necesidades particulares de tu aplicación.
Si prefiere flexibilidad de esquema en Mongoose, pase la propiedad strict: false en las opciones de esquema al configurar su esquema para la conexión secundaria. Esto le permite trabajar con datos que no se ajustan estrictamente al esquema.
Importe el archivo product.schema.js para acceder al esquema del producto. Esto le permite crear un modelo usando el objeto db y realizar operaciones relacionadas con los productos en su aplicación:
// Import Product Schema const secondaryProductSchema = require("./product.schema.js")({ collection: "products", strict: false // Pass configuration options if needed }); // Create Model const SecondaryProductModel = db.model("Product", secondaryProductSchema); // Wait for secondary connection to be ready before executing operations db.once('open', async () => { console.log('Secondary database (flexible schema) connected, executing operations...'); try { // Check if products already exist in secondary database const existingCount = await SecondaryProductModel.countDocuments(); console.log(`Existing products in secondary DB: ${existingCount}`); if (existingCount === 0) { // Add extra fields to demonstrate schema flexibility const flexibleSampleProducts = sampleProducts.map(product => ({ ...product, extraField: "This field is not in the schema but will be saved due to strict: false", timestamp: new Date() })); console.log('Inserting sample products with extra fields into secondary database...'); await SecondaryProductModel.insertMany(flexibleSampleProducts); console.log('Sample products with extra fields inserted into secondary database!'); } // Find and display a product let product = await SecondaryProductModel.findOne(); console.log('Product found in secondary DB (flexible schema):', product); // Display all products const allProducts = await SecondaryProductModel.find(); console.log(`Total products in secondary DB: ${allProducts.length}`); } catch (error) { console.error('Error with secondary database operations:', error); } });
Cambie de base de datos dentro de la misma conexión
Dentro de la configuración de la base de datos de tu aplicación, puedes alternar entre diferentes bases de datos utilizando el método db.useDb(). Este método te permite crear un nuevo objeto de conexión asociado a una base de datos específica mientras compartes el mismo pool de conexiones.
Este enfoque permite gestionar múltiples bases de datos dentro de la aplicación, utilizando una sola conexión y manteniendo contextos de datos distintos para cada base de datos.
Importa el archivo product.schema.js para acceder al esquema del producto. Esto te permite crear un modelo utilizando el objeto db y realizar operaciones relacionadas con productos en tu aplicación.
Ejemplo: Almacenar con base de datos independiente
Considera una plataforma de comercio electrónico donde varias tiendas operan de forma independiente, y cada tienda mantiene su propia base de datos para la gestión de productos. Usa el método db.useDb() para cambiar entre diferentes bases de datos de tiendas mientras mantienes un pool de conexiones compartido, como se muestra en el siguiente ejemplo:
// Load environment variables require('dotenv').config(); // Establish the secondary MongoDB connection const db = require("./db.secondary.js")(process.env.SECONDARY_CONN_STR, { // (optional) connection options }); // Import Product Schema const secondaryProductSchema = require("./product.schema.js")({ collection: "products", // strict: false // that doesn't adhere strictly to the schema! // Pass configuration options if needed }); // Base sample products data const sampleProducts = [ { name: "Laptop Pro", price: 1299.99, category: "Electronics", description: "High-performance laptop for professionals" }, { name: "Wireless Headphones", price: 199.99, category: "Electronics", description: "Premium noise-cancelling headphones" }, { name: "Coffee Maker", price: 89.99, category: "Kitchen", description: "Automatic drip coffee maker" } ]; // Sample store-specific products const storeAProducts = sampleProducts.map(product => ({ ...product, store: { name: "Store A" }, storeId: "A" })); const storeBProducts = [ { name: "Gaming Chair", price: 299.99, category: "Furniture", description: "Ergonomic gaming chair with RGB lighting", store: { name: "Store B" }, storeId: "B" }, { name: "Mechanical Keyboard", price: 149.99, category: "Electronics", description: "RGB mechanical gaming keyboard", store: { name: "Store B" }, storeId: "B" } ]; // Create a connection for 'Store A' const storeA = db.useDb('StoreA'); // Create Model const SecondaryStoreAProductModel = storeA.model("Product", secondaryProductSchema); // Wait for Store A connection to be ready storeA.once('open', async () => { console.log('Store A database connected, executing operations...'); try { // Check if products already exist in Store A const existingCount = await SecondaryStoreAProductModel.countDocuments(); console.log(`Existing products in Store A: ${existingCount}`); if (existingCount === 0) { console.log('Inserting sample products into Store A database...'); await SecondaryStoreAProductModel.insertMany(storeAProducts); console.log('Sample products inserted into Store A database!'); } // Find and display a product let product = await SecondaryStoreAProductModel.findOne(); console.log('Product found in Store A:', product); // Display all products const allProducts = await SecondaryStoreAProductModel.find(); console.log(`Total products in Store A: ${allProducts.length}`); } catch (error) { console.error('Error with Store A operations:', error); } }); // Create a connection for 'Store B' const storeB = db.useDb('StoreB'); // Create Model const SecondaryStoreBProductModel = storeB.model("Product", secondaryProductSchema); // Wait for Store B connection to be ready storeB.once('open', async () => { console.log('Store B database connected, executing operations...'); try { // Check if products already exist in Store B const existingCount = await SecondaryStoreBProductModel.countDocuments(); console.log(`Existing products in Store B: ${existingCount}`); if (existingCount === 0) { console.log('Inserting sample products into Store B database...'); await SecondaryStoreBProductModel.insertMany(storeBProducts); console.log('Sample products inserted into Store B database!'); } // Find and display a product let product = await SecondaryStoreBProductModel.findOne(); console.log('Product found in Store B:', product); // Display all products const allProducts = await SecondaryStoreBProductModel.find(); console.log(`Total products in Store B: ${allProducts.length}`); } catch (error) { console.error('Error with Store B operations:', error); } });
El ejemplo anterior establece conexiones de base de datos separadas para Store A y Store B, con cada tienda conteniendo sus propios datos de productos. Este enfoque proporciona separación de datos mientras utiliza un solo pool de conexiones compartido para una gestión eficiente de recursos.
El enfoque estático anterior crea conexiones explícitas para cada tienda con nombres predefinidos (StoreA, StoreB).
Para la gestión dinámica de tiendas, cree una función que acepte un identificador de tienda como parámetro y devuelva un objeto de conexión. Esta función permite cambiar de tienda por identificador y reutiliza las conexiones existentes para una mayor eficiencia.
// Function to get connection object for particular store's database function getStoreConnection(storeId) { return db.useDb("Store"+storeId, { useCache: true }); } // Create a connection for 'Store A' const store = getStoreConnection("A"); // Create Model const SecondaryStoreProductModel = store.model("Product", secondaryProductSchema); // Wait for store connection to be ready store.once('open', async () => { console.log('Store A (dynamic) database connected, executing operations...'); try { // Check if products already exist in the store const existingCount = await SecondaryStoreProductModel.countDocuments(); console.log(`Existing products in Store A (dynamic): ${existingCount}`); if (existingCount === 0) { // Use the same store A products from the previous example console.log('Inserting sample products into Store A (dynamic) database...'); await SecondaryStoreProductModel.insertMany(storeAProducts); console.log('Sample products inserted into Store A (dynamic) database!'); } // Find and display a product let product = await SecondaryStoreProductModel.findOne(); console.log('Product found in Store A (dynamic):', product); // Display all products const allProducts = await SecondaryStoreProductModel.find(); console.log(`Total products in Store A (dynamic): ${allProducts.length}`); } catch (error) { console.error('Error with Store A (dynamic) operations:', error); } });
En el enfoque dinámico, las instancias de conexión se crean y almacenan en caché según sea necesario, eliminando la necesidad de gestionar manualmente conexiones independientes para cada almacén. Este enfoque mejora la flexibilidad y la eficiencia en el uso de recursos en escenarios donde necesitas trabajar con múltiples tiendas en tu aplicación.
Próximos pasos
Este tutorial demuestra cómo implementar múltiples conexiones de MongoDB en una aplicación Node.js utilizando Mongoose. Aprendiste a establecer conexiones primarias y secundarias, implementar la flexibilidad de esquemas y gestionar múltiples bases de datos dentro de un único pool de conexiones.
Estas técnicas permiten la separación de datos, un mejor rendimiento y flexibilidad arquitectónica para aplicaciones que requieren múltiples contextos de datos. Ahora puede implementar estrategias de conexión que se ajusten a los requisitos específicos de su aplicación. Considere las siguientes prácticas recomendadas y recursos adicionales a medida que trabaja con múltiples conexiones de MongoDB.
Mejores prácticas
Al implementar múltiples conexiones de MongoDB en tu aplicación Node.js, sigue estas mejores prácticas:
Agrupamiento de conexiones: Utiliza agrupamiento de conexiones para gestionar eficazmente las conexiones de MongoDB. El agrupamiento de conexiones permite la reutilización de conexiones y reduce la sobrecarga. Para aprender más, consulte agrupamiento de conexiones en el manual del servidor y Gestionar conexiones con grupos de conexiones en la documentación del driver Node.js de MongoDB.
Manejo de errores: Implementa un manejo de errores adecuado, registros y mecanismos de recuperación para garantizar la fiabilidad de la conexión.
Seguridad: Implementar prácticas de autenticación, autorización y comunicación segura al manejar datos sensibles. Para obtener más información, consulta Protege tus datos.
Escalabilidad: Diseñe su estrategia de conexión para soportar los requisitos tanto de escalado horizontal como vertical.
Pruebas: Prueba tu configuración de conexiones múltiples bajo diversas condiciones, incluidos escenarios de conmutación por error, alta carga y restricciones de recursos.
Recursos adicionales
Para aprender más sobre cómo comenzar con Mongoose, consulte el tutorial Primeros pasos con Mongoose en la sección de Integraciones de terceros.
Para aprender más sobre cómo utilizar Mongoose con MongoDB, consulta la Documentación de Mongoose.