31

I am writing a web application that uses asynchronous database requests as a part of the api. Currently, I have an async express route that awaits function returns from async functions. Both of these functions return booleans and both query the database. One works correctly, however the second one does not.

Here is the MongoClient setup:

const MongoClient = require('mongodb').MongoClient;
const uri = config.uri;                         // Contains custom url for accessing database
const client = new MongoClient(uri, { useUnifiedTopology: true}, { useNewUrlParser: true }, { connectTimeoutMS: 30000 }, { keepAlive: 1});

where config is from a file imported as.

const config = require("./config.js");

and functions properly.

Here is the express setup:

app.post("/signup", async function(request, response) {
  log("POST request at /signup");

  log("BEFORE UNIQUE USER");
  const isUniqueUser = await validateUniqueUser(request.body.email, request.body.password);
  log(isUniqueUser);
  const status = {
    status: null
  };
  if (isUniqueUser) {
    log("AFTER UNIQUE USER");
    let userCreated = await createPracticeProfile(request.body.email, request.body.password);
    log("user created: " + userCreated);
    if (userCreated) {
      status.status = "user_created";
    }
    response.json(status);
  } else {
    response.json(status);
  }

  console.log("********************************end");
});

The console outputs:

BEFORE UNIQUE USER

true (which it should be)

AFTER UNIQUE USER

MongoError: Topology is closed.

user created: undefined

***...***end

Here is the function for validating that a user is unique:

/*  VALIDATE_UNIQUE_USER
USE: ensure user does not have existing profile
PARAMS: email (string), password (string)
RETURN: isUniqueUser (bool)
*/
async function validateUniqueUser(email, password) {
  // connect to database
  const database = await client.connect().catch(err => {
    log("ERROR while connecting to database at: validateUniqueUser");
    console.log(err);
    client.close();
  });

  // database connection failed
  if (!database) {
    return false;
  }

  // connection successful => find user
  let user;
  try {
    user = await database.db("guitar-practice-suite").collection("users").findOne({email: email});
  } catch(err) {
    log("ERROR while finding user in database at: validateUniqueUser");
    console.log(err);
    client.close();
    return false;
  } finally {
    client.close();
    // user not found (unique)
    if (user === null || user === undefined) {
      return true;
    }
    return false;
  }
}

Here is the function for inserting the user into the collections:

/* CREATE_PRACTICE_PROFILE
USE: insert a practice profile into the database
PARAMS: email (string), password (string)
RETURN: userCreated (bool)
*/
async function createPracticeProfile(email, password) {
  // hash password
  let hashedPassword;
  try {
    hashedPassword = await new Promise((resolve, reject) => {
      bcrypt.hash(password, null, null, function(err, hash) {
        if (err) {
          reject(err);
        }
        resolve(hash)
      });
    });
  } catch(err) {
    log("ERROR while hashing password at: createPracticeProfile");
    console.log(err);
    return false;
  }

  // connect to database
  const database = await client.connect().catch(err => {
    log("ERROR while connecting to database at: validateUniqueUser");
    console.log(err);
    client.close();
  });

  // database connection failed
  if (!database) {
    return false;
  }

  // database connection successful => insert user into database
  let insertUserToUsers;
  let insertUserToExercises;
  let insertUserToCustomExercises;
  try {
    insertUserToUsers = await database.db("guitar-practice-suite").collection("users").insertOne({email: email, password: hashedPassword});
    insertUserToExercises = await database.db("guitar-practice-suite").collection("exercises").insertOne({email: email});
    insertUserToCustomExercises = await database.db("guitar-practice-suite").collection("custom-exercises").insertOne({email: email, exercises: []});
  } catch(err) {
    log("ERROR while inserting user into database at: createPracticeProfile");
    console.log(err);
    client.close();
    return false;
  } finally {
    client.close();
    return insertUserToUsers && insertUserToExercises && insertUserToCustomExercises;
  }
}
CameronBurkholder
  • 752
  • 1
  • 6
  • 9

8 Answers8

31

I've found the solution to the problem, but I'm not sure I understand the reasoning. The client.close() in the finally block of the validateUniqueUser function. It was closing the connection before the connection in the createPracticeProfile function was finished inserting the user.

When that line is taken out, the function works.

CameronBurkholder
  • 752
  • 1
  • 6
  • 9
  • Same thing for me. Seems strange its in the [MongoDB docs](https://docs.mongodb.com/drivers/node/current/fundamentals/connection/)? – benmneb Aug 08 '21 at 02:13
8

The issue is client variable needs to be reinstantiated again,

const client = new MongoClient(uri, { useUnifiedTopology: true}, { useNewUrlParser: true }, { connectTimeoutMS: 30000 }, { keepAlive: 1});

Try putting this in start of createPracticeProfile, validateUniqueUser and other functions

vinay gosain
  • 171
  • 6
4

I was getting the error

MongoError: Topology is closed

because of the authentication problem

MongoEror: Authentication failed

In my case, the problem was with the password of my database. My password only contained numerical digits.

I changed the password to all characters and both the errors were solved.

myeongkil kim
  • 2,465
  • 4
  • 16
  • 22
Asif Mohammad
  • 49
  • 1
  • 5
3

Configure your client connection like below example

var MongoClient = require('mongodb').MongoClient;

var Server = require('mongodb').Server;

var mongoClient = new MongoClient(new Server('localhost', 27017));

mongoClient.open(function(err, mongoClient) {

  var db1 = mongoClient.db("mydb");

  mongoClient.close();
});
Prakash Karena
  • 2,525
  • 1
  • 7
  • 16
1

In my case - connecting to AtlasDB using the MongoClient - I had to whitelist the IP i was accessing the cluster from

0

I think your mongodb service is stopped, to start it Task Manager -> Services -> Mongodb -> RightClick -> Start

0

My code has been working fine for a long time and hasn't thrown this error before: MongoError: Topology is closed.

But due to the fact that my laptop was turned on for a long time and I was simultaneously developing other projects on it, while the main one was running in the terminal, mongo most likely did not close one of the connections to the database and opened another in parallel, creating some kind of collision.

In general, in my case, the usual restart of the computer helped and a similar error did not occur again.

Ihor
  • 131
  • 1
  • 2
0

We just need to set allow access from anywhere and it worked for me. :)

STEP 1 - Go to Security -> Network Access.
STEP 2 - Click on edit button if any ip address is listed there else click on green Add IP Address button.
STEP 3 - In the pop modal, click on ALLOW ACCESS FROM ANYWHERE button, you should see 0.0.0.0/0 in access entry list.
STEP 4 - Save

:)

Sagar Darekar
  • 982
  • 9
  • 14