I just migrated from Mongoose 3 to 5.1.5 and have some issues. I have a nodejs application running trying to connect to multiple DBs on different hosts.
Connection.js file : This is used to maintain the connections.
I am exporting my connections to my models and binding the schemas
Connection.js file
const mongoose = require('mongoose');
const Mongoose = mongoose.Mongoose;
mongoose.Promise = require('bluebird');
-
-
const _connect = function (mongoUrl, options) {
mongoose.connect(mongoUrl, options).then(
() => { console.log('MongoDB Connected.'); },
err => { console.log('MongoDB not Connected.'); }
);
}
-
module.exports = {
conn1: new Connection('DB1'),
conn2: new Connection('DB2')
};
In model.js
I have created different models and each is saved as a different file.
const mongoose = require('mongoose');
const connections = require('./Connections');
-
-
const schema = new mongoose.Schema(model);
if (fileName.toLowerCase().includes('db2')) {
connections.conn2.model(fileName, schema);
} else {
connections.conn1.model(fileName, schema);
}
The whole setup was working properly in 3.x, but in 5.1.5, i get an issue
"connections.conn1.model is not a function"
To test the whole scenario, I commented one connection and gave my exports as below in Connection.js:
module.exports = {
mongoose: new Connection('DB1'),
};
and in model.js I just had
mongoose.model(fileName, schema);
which works perfectly. Please let me know what am I doing wrong.
Related
I have a NodeJs app using express and MongoDB that I want to write unit tests for using Jest.
Here's the problem: I'm importing a specific function from the file reports.js. The function itself doesn't use the mongoose db, but I know that using require executes the entire file before returning the exported objects. That being said, when I'm running my test file, the "testing" part works fine, but I'm getting the reference error below since the importation is still in process when my tests are completed.
ReferenceError: You are trying to import a file after the Jest environment has been torn down. From tests/reportsLib.test.js.
I've done some research and a lot suggests to use jest.useFakeTimers() before running each tests. But the same error occurs. However, when I use jest.useFakeTimers() after importing mongoose in the payment.js file (which is not optimal since I would like to have everything about tests in the tests files), the error is fixed but another occurs on the following line: campaignstatsSchema.plugin(mongoose_delete, {deletedAt:true, overrideMethods: true})
TypeError: Invalid schema configuration: -ClockDate- is not a valid type at path -deletedAt-. See mongoose-schematypes for a list of valid schema types.
Another way to fix the 1st error is to import the function in a beforeAll() Jest function, but the 2nd error still occurs.
Is there some sort of refac needed for the way I'm connecting to my DB and creating my schemas/models? What's the best way to solve this issue? Thanks in advance!
reports.tests.js file:
const { functionToTest } = require('./../lib/reports');
describe('report.js testing', () => {
beforeEach(() => {
jest.useFakeTimers();
});
it('should return a value of 0', () => {
expect(functionToTest()).toBe(0);
});
});
reports.js file:
const db = require('./../services/db');
// ...other imports
const functionToTest = function() {
return 0;
};
const otherFunction = async function(report_id, report_data) {
//some code
await db.report.findOneAndUpdate({_id: report_id}, {data:report_data});
};
module.exports = {
functionToTest,
// other functions
};
db.js file:
const mongoose = require('mongoose');
const payment = require('../models/payment');
const report = require('../models/report');
mongoose.connection.setMaxListeners(0);
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
});
const generateObjectId = function() {
return mongoose.Types.ObjectId();
};
const getTimestampFromId = function(id) {
return mongoose.Types.ObjectId(id).getTimestamp();
};
module.exports = {
report,
payment,
// other models
};
payment.js file:
const mongoose = require('mongoose');
const mongoose_delete = require('mongoose-delete');
const mongoose_history = require('mongoose-history');
const paymentProfileSchema = mongoose.Schema({
// fields here
});
paymentProfileSchema.plugin(mongoose_history);
paymentProfileSchema.plugin(mongoose_delete);
const paymentProfile = mongoose.model('payment-profiles', paymentProfileSchema);
paymentProfile.syncIndexes();
module.exports = paymentProfile;
I'm writing a Node.js cli in which I've to read from one Mongo Atlas DB and write to another Mongo Atlas DB. I'll be reading documents from one db and writing equivalent documents in the other db, one document at a time. I've two separate connection files like this:
ReadDB.js:
require('dotenv').config();
const mongoose = require('mongoose');
const read_db_url = process.env.READDB_URI;
const readDB = async () => {
try {
await mongoose.connect(read_db_url,
{
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: "dbProd"
}
);
} catch (err) {
console.error(err);
}
}
module.exports = readDB
WriteDB.js:
require('dotenv').config();
const mongoose = require('mongoose');
const write_db_url = process.env.WRITEDB_URI;
const writeDB = async () => {
try {
await mongoose.connect(write_db_url,
{
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: "dbQA"
}
);
} catch (err) {
console.error(err);
}
}
module.exports = writeDB
This what I've so far for the main application (cli.js):
cli.js:
require('dotenv').config();
const mongoose = require('mongoose');
const connectReadDB = require('./ReadDB.js');
const connectWriteDB = require('./WriteDB.js');
connectReadDB();
connectWriteDB();
const findProduct = async (productId) => {
products = await Products.find({_id:productId});
}
I guess my confusion is how Node.js will know which db to read from to begin with? Will I need separate set of models, one for read and one for write? How can I establish two simultaneous connections in the same Node.js app?
Mongoose handling connections via connections pool http://mongoosejs.com/docs/connections.html
You can use server: {poolSize: 5} option for increase/decrease pool (number of parallel connections)
If you need connections to different databases look here Mongoose and multiple database in single node.js project
Example of multiple connections:
const mongoose = require('mongoose')
const connection = mongoose.createConnection('mongodb://localhost/db1');
const connection2 = mongoose.createConnection('mongodb://localhost/db2');
const Schema = new mongoose.Schema({})
const model1 = connection.model('User', Schema);
const model2 = connection2.model('Item', Schema);
model1.find({}, function() {
console.log("this will print out last");
});
model2.find({}, function() {
console.log("this will print out first");
});
I have been working on this project for 2 years now, and I'm thinking this was caused by the recent update, but am wondering if there are any kind, intelligent, Mongoose/NoSQL DBA, souls out there who would do the awesome service of helping me either track-down, and/or resolve this issue.
So, as you can see below, this is a simple mongoose find query over express to MongoDB. This is rather evident, at a high-level, and for most devs, the interactions will be natural, as any Mongo, Express, Node Stack using Mongoose.
The is issue is that, when I send this query, disregarding environment (a production project), it does not resolve.
The "data" seems to get lost somewhere, and therefore, the query simply never resolves.
It's a simple setup, really a test endpoint, so help out, run it through, and send some feedback.
Greatly Appreciated!
Model.js
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate');
const Schema = mongoose.Schema;
const TestSchema = new Schema({
data: {
type: String,
unique: false,
required: true
},
}, {
timestamps: true
});
TestSchema.plugin(mongoosePaginate);
module.exports = mongoose.model('Test', TestSchema);
Constructor.js
class Constructor {
constructor() {}
getAll() {
return TestSchema.find({}, function (err, tests) {})
}
}
module.exports = Constructor
db.js
let mongoose = require('mongoose')
// Connect to db
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true }, err => {
if (err)
return console.log("Cannot connect to DB")
connectionCallback()
console.log("DB Connected")
});
let connectionCallback = () => {}
module.exports.onConnect = cb => {
connectionCallback = cb
}
App.js
const express = require('express');
const app = express();
const ip = require('ip');
let db = require('./db')
const router = express.Router();
const port = 8888;
const http = require('http').createServer(app);
let ipAddress = 'localhost'; // only works to the local host
try {
// will enable the server to be accessed from the network
ipAddress = ip.address();
} catch( err ){
console.err( err );
}
http.listen(port, ipAddress,
() => {
let message = [
`Server is running at ${ipAddress}:${port}`,
];
console.log( ...message )
});
db.onConnect(() => {
let Constructor = require("./pathTo/Constructor")
let construct = new Constructor()
app.use('/api', router.get('/test', function(req, res) {construct.getAll()}))
})
Your problem is with the constructor.js getAll function, as you are returning also and passed a callback also, the promise will never be resolved. You should either resolve the promise or return the response from the callback.
Resolve Promise:
class Constructor {
constructor() {}
async getAll() {
return await TestSchema.find({})
}
}
module.exports = Constructor
Return from callback:
class Constructor {
constructor() {}
getAll() {
TestSchema.find({}, function (err, tests){
return tests.
})
}
}
module.exports = Constructor
I ended up just scaling the project for production. I put the connectionCallback in a class and called it with the createConnection mongoose function.
Looks like this:
mongoose.Promise = global.Promise;
const url = 'mongodb://localhost/db'
const connection = mongoose.createConnection(url, options);
//load models
require('/models').connectionCallback();
modules.export = connectionInstance;
Please note, I am no longer using express!
I am having troubles after moving all of my Controllers Routes and Models into their own files. I seem to only get a timeout when loading anything from the database and none of my console.log()'s run anymore, The model works and it posts the testPost (in the controller) perfectly fine. I tried adding the testPost because console.log() or errors aren't showing in my console.
Controller
//jshint esversion: 6
const mongoose = require('mongoose');
const Post = require(`./../models/posts`);
//Initial Post to posts Model
const testPost = new Post ({
postTitle: `Default Post`,
postDate: Date.now(),
postBody: `If you're seeing this, It means your DB is connected and you're ready to go!`,
postAuthor: `Admin`
});
const defaultPost = [testPost];
//View Single Post
exports.showOne = function(req, res){
const requestedPostTitle = req.params.id;
Post.findOne({postTitle: requestedPostTitle}, function(err, foundPost){
if (!err && foundPost){
const title = foundPost.postTitle;
const date = foundPost.postDate;
const content = foundPost.postBody;
const author = foundPost.postAuthor;
res.render(`./post/post`, {
title: title,
date: date,
content:content,
author:author
});
}
});
};
Model
//jshint esversion: 6
const mongoose = require(`mongoose`);
const postSchema = new mongoose.Schema ({
{SCHEMA DATA}
}
});
module.exports = mongoose.model(`Post`, postSchema);
exports.databaseName = `postsDB`;
index.js Routes
app.get(`/posts`, postPages.showAll);
app.get(`/post/:id`, postPages.showOne);
app.get(`/post/compose`, postPages.compose);
app.post(`/post/compose`, postPages.create);
Firstly I moved my database startup into it's own file called db.js as per this great article that was referenced to me by another SO user:
https://theholmesoffice.com/mongoose-connection-best-practice/
After moving the configurations into db.js I logged the errors as per the article:
// If the connection throws an error
mongoose.connection.on('error',function (err) {
console.log('Mongoose default connection error: ' + err);
});
I had not realised that I wouldn't get error logs on database startup without this.
Make sure to send a request only after the connection to the DB was established.
I am currently trying to attach a global Mongoose on runtime with no luck. My plugin requires a few dependencies and options generated upon my app's bootstrapping thus I need to add it sequentially. Mongoose seems to ignore everything wrapped within a closure.
const mongoose = require('mongoose');
const config = {};
const {DB_CONNECT} = process.env;
const myPlugin = schema => {
console.log('done'); // this line is not logged at all
schema.methods.mymethod = () => {};
}
const connectAndAddPlugins = async () => {
await mongoose.connect(
DB_CONNECT,
{...config}
);
mongoose.plugin(myPlugin)
};
connectAndAddPlugins();
Any help will be highly appreciated.
Apparently, since a model gets compiled and loaded with Mongoose global plugins are not attached anymore thus models should get registered afterwards:
const mongoose = require('mongoose');
const config = {};
const {DB_CONNECT} = process.env;
const myPlugin = schema => {
console.log('done'); // this line is not logged at all
schema.methods.mymethod = () => {};
}
const connectAndAddPlugins = async () => {
await mongoose.connect(
DB_CONNECT,
{...config}
);
mongoose.plugin(myPlugin)
};
const loadModels = () => {
const model = mongoose.model('Cat', { name: String });
}
connectAndAddPlugins();
loadModels();