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.

Answer

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).