mongoose async await, unable to capture connection state - node.js

I'm trying to capture the connection success state for mongoose/mongodb.
The idea is simply that, when I start the app, if the connection fails, I need an alert recorded.
I can't figure out why, once I connect - or once the connection fails - the function does not return what I tell it to return.
import { DBURL } from '../parameters/environment';
const mongoose = require('mongoose');
const chalk = require('chalk');
const connected = chalk.bold.cyan;
const error = chalk.bold.yellow;
const connectMe = async () => {
await mongoose.connect(DBURL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
console.log(connected('DB connection successful'));
return 'Success';
})
.catch((reason) => {
console.log(error('Unable to connect to the mongodb instance. Error: '), reason);
return 'FAIL';
});
return 'Why am I returning this????';
};
module.exports = connectMe;
I simply call it and try to display the result.
But regardless of the DB state, the return statement in .then or .catch is ignored
Server.js:
const connectMe = require('./db-actions/db-connect');
const myResult = connectMe();
myResult.then(x => console.log(x));
If mongodb is up, I get this:
DB connection successful
Why am I returning this????
If mongodb is down, I get this:
Unable to connect to the mongodb instance. Error: MongooseServerSelectionError: connect ECONNREFUSED 127.0.0.1:30000
Why am I returning this????
Console.log works, but the return does not.
Any idea why?

Why it seems return is not working:
You are combining both async/await and .then/.catch, you should choose one of the two. Also, the return keyword is not properly placed within connectMe the function.
The fix:
Since, you are expecting the connectMe function to return a promise so you can attach a .then like this:
myResult.then(x => console.log(x));
You can just go with a .then/.catch in the connectMe function and the return statement should be on mongoose.connect, that's the promise the outside world(i.e outside the function) needs to interact with. Code:
const connectMe = () => {
return mongoose.connect(DBURL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => {
console.log(connected('DB connection successful'));
return 'Success';
})
.catch ((reason) => {
console.log(error('Unable to connect to the mongodb instance. Error: '), reason);
return 'FAIL';
})
};

Related

NodeJS Why is Mongoose disconnecting? (And only when I execute a file mind you)

Schema operations in routes proceed without a hitch. But when I reuse the same code (just a find() call for example) and execute it in a file ('node test.js'), I get - "MongooseError: Operation refreshtokens.findOne() buffering timed out after 10000ms."
So I tried checking mongoose.connection.readyState and sure enough it equals 0 (disconnected). Why? When again, mongoose.connection is never touched in my routes and I've never had any issues there (it's only initialized in server.js).
Ultimately I'm trying to create an executable that I'll schedule, and it will need to access the database.
Nothing unusual about the code (I don't think ;) -
server.js
const express = require('express')
const app = express()
const mongoose = require('mongoose')
mongoose.connect(process.env.MONGODB_URI, {useNewUrlParser: true, useUnifiedTopology: true})
const db = mongoose.connection
db.on('error', (error) => console.error(error))
db.once('open', () => console.log('Connected to Database'))
...
./models/refreshtoken.js
const mongoose = require('mongoose')
const refreshTokenSchema = new mongoose.Schema({
token: {
type: String,
required: true
}
})
module.exports = mongoose.model('RefreshToken', refreshTokenSchema)
Test executable (test.js)
const RefreshToken = require('./models/refreshtoken.js');
const mongoose = require('mongoose');
(async () => {
await testGetToken();
console.log("ran testGetToken()");
})().catch(e => {
// Deal with the fact the chain failed
console.log(e)
});
async function testGetToken() {
try {
console.log("here in testGetToken() call")
console.log("pre if mongoose.connection = " + mongoose.connection.readyState);
if (mongoose.connection.readyState != 1) {
await mongoose.connect(process.env.MONGODB_URI, {useNewUrlParser: true, useUnifiedTopology: true})
}
console.log("post if mongoose.connection = " + mongoose.connection.readyState);
const token = await RefreshToken.find()
console.log(token)
} catch (err) {
console.log("testGetToken() err = " + err)
}
}
The executable will get and print the token after I added in the if statement with await mongoose.connect but then the process doesn't close out (I have to ctrl-c twice). Why? (And as noted at top, without the await mongoose.connect, I get the MongooseError.)
Best thing to know is why does mongoose have a mind of its own and is disconnecting only in files I execute (and how to correct it). Added mongoose.connection.on('disconnected'...) event code in server.js too and it never trips.
Next best thing would be if we didn't expect any deleterious effects in proceeding as such, how to get the executable's process to close out. I can do more homework here but I tend to think it's best to resolve problems rather than ignore them (see: Best thing)? :)
Appreciate any help you can offer. Thank you.
It either has an error you aren’t catching or can’t find one. I’ve had the same problem and in my case I was misusing the findOne() an it wasn’t matching up. Try using a mongo GUI and console.log() for you results.
I think I figured this out. In a one-off script, you need to connect and close a separate mongoose connection (with mongoose.connect and mongoose.connection.close).
I thought it would leverage my existing mongoose connection, especially when I had put the test.js code in (and was executing) my route file, but even there not the case. It must be that a heroku scripting process (or what would this be called?) is separate from a web process.
I also thought if I had mongoose.connection.close() in my script, again thinking they were one in the same, then it would take that connection down and schema calls in my route would fail. Also not the case.
Updated test.js for your reference -
const RefreshToken = require('./models/refreshtoken.js');
const mongoose = require('mongoose');
// ALWAYS executes (think of this like your main)
(async () => {
await mongooseConnect()
await testGetToken();
mongoose.connection.close()
})().catch(e => {
console.log(e)
});
async function mongooseConnect() {
// This is what I was missing before. I thought it would leverage my existing mongoose connection from server.js
// but it most certainly would not when running a script
await mongoose.connect(process.env.MONGODB_URI, {useNewUrlParser: true, useUnifiedTopology: true})
}
async function testGetToken() {
// gets token from database and prints
try {
console.log("here in testGetToken() call")
const token = await RefreshToken.find()
console.log(token)
} catch (err) {
console.log("testGetToken() err = " + err)
}
}
// event code that fires when mongoose is connected
mongoose.connection.on('connected', () => {
console.log('Test script connected to database');
console.log(mongoose.connection.readyState); //logs 1
});
// event code that fires when mongoose is disconnected
mongoose.connection.on('disconnected', () => {
console.log('Test script disconnected from database');
console.log(mongoose.connection.readyState); //logs 0
});
Output when I run from my command line 'heroku run node test.js' -
Test script connected to database
1
here in testGetToken() call
[
{
_id: 61355917fedd0f00166e4e08,
token: <mytokenhere>,
__v: 0
}
]
Test script disconnected from database
0
Final key point would be I also have the same "mongoose.connection.on('disconnected'..." code in my server.js with a different log message and it doesn't show up in the server logs after running this script (that was one of my fears, that the connection.close in script would take my server mongoose connection down; it doesn't).
Sometimes it also as simple as checking if your current ip is whitelisted or not.

Unhandled Rejection (TypeError): mongoose.connect is not a function

I've tried a bunch of different changes, but nothing seems to be working. I've tried switching to an import statement. I've tried putting the code in App.js. I've tried using the mongodb library instead of mongoose. I've followed a dozen tutorials on connecting to mongodb. For some reason, the connect statement keeps throwing this error. What am I missing?
const mongoose = require('mongoose');
export async function post({username, password}) {
const data = {username, password};
const uri =
'mongodb+srv://[username removed]:[password removed]#[project name removed].krcm7.mongodb.net/database?retryWrites=true&w=majority';
mongoose
.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then((result) => console.log('connected to db'))
.catch((err) => console.log(err));
return;
}
To add context, this is how the function is being called.
function submitForm(username, password) {
post(username, password).then(() => {
console.log('post function ran');
});
}
I found the problem. Mongodb and mongoose are designed to work specifically in node.js and won't work in a browser environment.

Unhandled promise rejection. Can't connect to server

I was wondering what I'm doing wrong here.I couldn't connect to the server and gives me a message that a promise was rejected and was not handled by catch(). Am i missing an async function somewhere? Thanks in advance.
const mongoose = require("mongoose");
const Dishes = require("./models/dishes");
const url = "mongodb://localhost:27017/conFusion";
const connect = mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
connect.then(db => {
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
newDish
.save()
.then(dish => {
console.log(dish);
return Dishes.find({});
})
.then(dishes => {
console.log(dishes);
return Dishes.remove({});
})
.then(() => {
return mongoose.connection.close();
})
.catch(err => {
console.log(err);
});
});
You have no rejection handler on the promise from mongoose.connect itself (your connect constant). you only use then, not catch, and you don't supply the second argument to then.
So the minimum change is:
connect.then(db => {
// ...
})
.catch(error => { // ***
// ...handle/report error connecting... // ***
}); // ***
Am i missing an async function somewhere?
No, but using one might make the code easier to follow (this is subjective). The purpose of async/await is to make it possible to write code with our usual flow-control structures while using promises.
For instance, if you can't use top-level await or you don't want to, you could wrap all your code in an immediately-invoked async function like this:
const mongoose = require("mongoose");
const Dishes = require("./models/dishes");
const url = "mongodb://localhost:27017/conFusion";
// If you can't use top level `await`, you can use an `async` IIFE
(async () => {
const connect = await mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
const dish = await newDish.save();
console.log(dish);
const dishes = await Dishes.find({});
console.log(dishes);
await Dishes.remove({});
await mongoose.connection.close();
})().catch(error => {
// ...handle/report error...
});
If you can use top-level await and you want to (it does mean you have to switch to using JavaScript's own module syntax instead of CJS modules, but IMHO that's a good thing anyway), you can do this:
import mongoose from "mongoose";
import Dishes from "./models/dishes.js";
const url = "mongodb://localhost:27017/conFusion";
try {
const connect = await mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
const dish = await newDish.save();
console.log(dish);
const dishes = await Dishes.find({});
console.log(dishes);
await Dishes.remove({});
await mongoose.connection.close();
} catch (error) {
// ...handle/report error...
}
Note, though, that you'd probably only want to do that in your top-level module (which it looks like this is), since it holds up resolution of the module tree. That's fine for the entry point module, or a module that can't create its exports until a promise settles, but for the logic in the code you've shown it would probably only be appropriate in the entry point module.

How to use async/await with mongoose

In node.js I had code like following:
mongoose.connect(dbURI, dbOptions)
.then(() => {
console.log("ok");
},
err => {
console.log('error: '+ err)
}
);
Now i want to do it with async/await syntax. So i could start with var mcResult = await mongoose.connect(dbURI, dbOptions);, afaik it will wait for operation, until it ends with any result (much like calling C function read() or fread() in syncronous mode).
But what should I write then? What does that return to the mcResult variable and how to check for an error or success? Basically I want a similar snippet, but written with proper async/await syntax.
Also I wonder because I have auto reconnect, among dbOptions:
dbOptions: {
autoReconnect: true,
reconnectTries: 999999999,
reconnectInterval: 3000
}
Would it "stuck" on await forever, in case if database connection is unavailble? I hope you can give me a clue on what would happen and how that would work.
Basically I want a similar snippet, but written with proper async/await syntax.
(async () => {
try {
await mongoose.connect(dbURI, dbOptions)
} catch (err) {
console.log('error: ' + err)
}
})()
Please try this, Below code has basics of db connectivity and a query :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let url = 'mongodb://localhost:27017/test';
const usersSchema = new Schema({
any: {}
}, {
strict: false
});
const Users = mongoose.model('users', usersSchema, 'users');
/** We've created schema as in mongoose you need schemas for your collections to do operations on them */
const dbConnect = async () => {
let db = null;
try {
/** In real-time you'll split DB connection(into another file) away from DB calls */
await mongoose.connect(url, { useNewUrlParser: true }); // await on a step makes process to wait until it's done/ err'd out.
db = mongoose.connection;
let dbResp = await Users.find({}).lean(); /** Gets all documents out of users collection.
Using .lean() to convert MongoDB documents to raw Js objects for accessing further. */
db.close(); // Needs to close connection, In general you don't close & re-create often. But needed for test scripts - You might use connection pooling in real-time.
return dbResp;
} catch (err) {
(db) && db.close(); /** Needs to close connection -
Only if mongoose.connect() is success & fails after it, as db connection is established by then. */
console.log('Error at dbConnect ::', err)
throw err;
}
}
dbConnect().then(res => console.log('Printing at callee ::', res)).catch(err => console.log('Err at Call ::', err));
As we're talking about async/await then few things I wanted to mention - await definitely needs it's function to be declared as async - otherwise it would throw an error. And it's recommended to wrap async/await code inside try/catch block.
const connectDb = async () => {
await mongoose.connect(dbUri, dbOptions).then(
() => {
console.info(`Connected to database`)
},
error => {
console.error(`Connection error: ${error.stack}`)
process.exit(1)
}
)
}
connectDb().catch(error => console.error(error))
Lets assume the use of then() is prohibited, you can result to this...
const connectDb = async () => {
try {
await mongoose.connect(dbConfig.url, dbConfigOptions)
console.info(`Connected to database on Worker process: ${process.pid}`)
} catch (error) {
console.error(`Connection error: ${error.stack} on Worker process: ${process.pid}`)
process.exit(1)
}
}

MongoClient throw MongoError: server instance pool was destroyed

I saw these posts on SO describing this error. Most of them was by the reason that JavaScript is async and mongoClient.close() called outside of callback. That's not my case, but I don't know what else can be the reason.
const MongoClient = require('mongodb').MongoClient;
const url = "mongodb://localhost:27017/";
const mongoClient = new MongoClient(url, {
useNewUrlParser: true
});
module.exports = class Mongo {
insertOne(article) {
mongoClient.connect((err, client) => {
const db = client.db('grabber');
db.collection("zr").insertOne(article, (err, res) => {
if (err) throw err;
mongoClient.close();
});
});
};
}
I observed that you open mongoClient.connect() in the insertOne() method, and also call mongoClient.close() within that method, with mongoClient as a global variable.
My hunch is that either:
There's another method that calls mongoClient that was closed by this method, or
You called the insertOne(article) twice
I can confirm that the second reason is the most likely one. Here's the code I tried:
const MongoClient = require('mongodb').MongoClient;
const url = "mongodb://localhost:27017/";
const mongoClient = new MongoClient(url, {
useNewUrlParser: true
});
class Mongo {
insertOne(article) {
mongoClient.connect((err, client) => {
const db = client.db('grabber');
db.collection("zr").insertOne(article, (err, res) => {
if (err) throw err;
mongoClient.close();
});
});
};
};
x = new Mongo()
setTimeout(function() { x.insertOne({'a': 1}); }, 1000);
setTimeout(function() { x.insertOne({'a': 2}); }, 2000);
The two setTimeout was there to ensure that the two insertOne() are called one after another. Result:
MongoError: server instance pool was destroyed
The way your code is currently structured, the node driver creates a new connection pool every time insertOne() is called. This is not optimal, and prevents the node driver to utilize connection pooling.
Instead of calling mongoClient.connect() inside insertOne(), call it globally outside of class Mongo. Pass the global connection object (the returned object from mongoClient.connect()) instead of the mongoClient object itself to your insertOne() method.

Resources