Raold
Raold

Reputation: 1493

Mongoose - multiple database connections

I want to understand how to switch between databases within mongoose global promise connection.

My current connection is established this way app.ts

import * as mongoose from 'mongoose';

...

try {
    await mongoose.createConnection(`mongodb://localhost:27017/db1`, {
        useNewUrlParser: true,
    })
    console.log("Connected")
} catch (error) {
    console.log(error)
}

And then I am accessing it in different files some.model.ts

import { Schema, Document, model } from 'mongoose';

const SomeSchema: Schema = new Schema({
  name: { type: String, required: true },
  owner: { type: string, required: true }
});
export default model('Some', SomeSchema);

According to documentation.

So far we've seen how to connect to MongoDB using Mongoose's default connection. At times we may need multiple connections open to Mongo, each with different read/write settings, or maybe just to different databases for example. In these cases we can utilize mongoose.createConnection() which accepts all the arguments already discussed and returns a fresh connection for you. const conn = mongoose.createConnection('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', options);

I can create multiple database connections like this

try {
const db1 = await mongoose.createConnection(`mongodb://localhost:27017/db1`, {
    useNewUrlParser: true,
})
const db2 = await mongoose.createConnection(`mongodb://localhost:27017/db2`, {
    useNewUrlParser: true,
})
    console.log("Connected")
} catch (error) {
    console.log(error)
}

I can see both connection in console.log(mongoose.connections)

But how can I specify what database should be used for the Model in some.model.ts?

import { Schema, Document, model } from 'mongoose';

const SomeSchema: Schema = new Schema({
  name: { type: String, required: true },
  owner: { type: string, required: true }
});

export default SPECIFY_DATABASE.model('Some', SomeSchema);

I have found other questions like this but there are connections created "localy", I need to use mongoose connection across many different files.

Thank you for answers, if you need more explanation please let me now.

Upvotes: 6

Views: 12096

Answers (1)

Zlatko
Zlatko

Reputation: 19569

You need to actually return the connection, and then register a given model to each of the connections. To clarify, you need:

  • something to create a (named, specific) connection
  • schemas
  • you create models by registering schemas to the given connections,
  • you also need something to orchestrate it.

Example, lets have a "db.js" file (I call mine "repo.js" usually) with a single export, a function that returns the initialized database Promise. You'd use it by importing the function and awaiting for the db.

I have a bit of a longer example, so error handling etc is ommited for brevity.

import { createConnections } from './create-connections';
import { UsersSchema } from './users-schema';
import { PostsSchema } from './posts-schema';

let db: any;

export function getDatabase(): Promise<any> {
    if (this.db) return Promise.resolve(db);
    return createDatabases();
}

async function createDatabases() {
    const { db1, db2 } = await createConnections('mongodb://localhost/db1', 'mongodb://localhost/db2');
    const UserModel = db1.model('users', UserSchema);
    const PostModel = db2.model('posts', PostSchema);
    db = {
      UserModel,
      PostModel,
      // also if you need this
      connections: {
        db1,
        db2,
      }
    }
    return db;
}

Now, I've used './create-connections' here, which is almost what you have:

// create-connection.js
const { createConnection } = require('mongoose');

// You create connections by calling this function and giving it the URL to the server
export function createConnections(url1, url2) {
  const db1 = await createConnection(url1);
  const db2 = await createConnection(url2);
  return {
    db1,
    db2
  }
}

Now, let's say you have two models: users and posts, let's have their schemas.

// users schema
import { Schema, Document } from 'mongoose';
export const UserSchema: Schema = new Schema({
  name: { type: String, required: true },
});

// posts schema
import { Schema, Document } from 'mongoose';
export const PostSchema: Schema = new Schema({
  text: { type: String, required: true },
  owner: { type: SchemaID, required: true }
});

So now you need to bind it all in that fdirst file.

But how to use it? As I've said, since it's async, you always import it and use it as a simple async getDB:

// some controller, route handler, service etc.
import { getDatabase } from './get-database';

router.get('/users', async (req, res) => {
  const User = await getDatabase().UserModel;
  const users = await User.find();
  return res.json(users);
});

router.post('/posts', async (req, res) {
  const { text } = req.body;
  const owner = req.user.id;
  const Post = await getDatabase().PostModel;

  const post = await Post.create({ text, owner });
  return res.json(post);      
});

Upvotes: 13

Related Questions