Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.96k stars 3.84k forks source link

Lazy connection handling while registering new models #14089

Closed noghartt closed 11 months ago

noghartt commented 12 months ago

Prerequisites

Mongoose version

7.6.3

Node.js version

18.16.0

MongoDB version

5.8.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

14.1.0

Issue

I have a monorepo where I handle 2+ connections accordingly to the entrypoints.

In case, I have a specific flow where a model registered via mongoose.model runs before the connection to the DB.

// packages/A/Model.ts
// ...
const model = mongoose.model('Model', ModelSchema);

If a try to do something like: connection.model('Model', ModelSchema), it throws an error because connection is undefined until connnect.

// packages/A/Model.ts
// ...
import { getConnection } from '@packages/mongodb';

const connection = getConnection('OtherDatabase'); // get connection accordingly to the `mongoose.connections`

const model = connection.model('Model', ModelSchema);

I would like to know if there's a way/strategy to handle this kind of "model registry" in a multi DB architecture allowing this lazy code evaluation.

sibelius commented 12 months ago

can we be able to define the collection name and the database name in the SchemaModel ?

Schema.collectionName = 'ok': Schema.databaseName = 'another-database';

vkarpov15 commented 11 months ago

I typically end up with a function that I use to create a connection and attach models. That's usually the way to go when you have multiple connections. Something like the following:

const connectionString = process.env.MONGODB_CONNECTION_STRING;
console.log('Connecting to', connectionString);

module.exports = function models(connection) {
  connection = connection ?? mongoose.createConnection(connectionString, {
    serverSelectionTimeoutMS: 5000
  });

  initTask(null, connection);

  for (const [schemaName, schema] of Object.entries(schemas)) {
    // "accessTokenSchema" -> "AccessToken"
    const modelName = schemaName.charAt(0).toUpperCase() +
      schemaName.replace(/Schema$/, '').slice(1);
    connection.model(modelName, schema, modelName);
  }

  return connection;
};

Does that help?

sibelius commented 11 months ago

if have something like this

export const getConnection = (connectionName: string) => {
  const connection = mongoose.connections.find(
    (c) => c.name === connectionName,
  );

  if (!connection) {
    return mongoose;
  }

  return connection;
};

export const getModelBySchema = (schema: SchemaMultiDB) => {
  const connection = getConnection(schema.databaseName);

  return connection.model(schema.name, schema);
};

the usage is

const Event = getModelBySchema(EventSchema);

We would like to keep using just EventModel instead of having a lazy function to get the model from a custom connection

vkarpov15 commented 11 months ago

@sibelius that patterns works as well. It's not my favorite pattern, but it's the only real option if you have multiple connections and want to be able to import models via require() / import.