4
votes

I'm working on a Node.js server, connecting to MongoDB and written with TypeScript. When I try to use MongoDB it doesn't create a connection, however when I check the mongo output it does appear to create a connection.

What am I missing in my code to define the connection in Node.js?

My connection string is 'mongodb://localhost:27017'

My connection method:

connect() {
    console.log('connecting to mongo') //this is called
    MongoClient.connect(this.connectionString, {useNewUrlParser: true})
    .then(client => {
        console.log('setting client'); //this doesn't get called
        this.client = client;
        console.log(this.client);
    })
    .catch(error => {
        console.log('error during connecting to mongo: '); //this also doesn't get called
        console.error(error);
    });
}

Mongo output:

2018-11-08T23:06:24.106+0100 I NETWORK [listener] connection accepted from 127.0.0.1:51345 #11 (2 connections now open)

2018-11-08T23:06:24.107+0100 I NETWORK [conn11] received client metadata from 127.0.0.1:51345 conn11: { driver: { name: "nodejs", version: "3.1.9" }, os: { type: "Darwin", name: "darwin", architecture: "x64", version: "17.7.0" }, platform: "Node.js v8.9.3, LE, mongodb-core: 3.1.8" }

My repository is at https://github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/db/mongo.db.ts for the full code.

console.log(db) as asked by shkaper

MongoClient {
  domain: null,
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined,
  s:
   { url: 'mongodb://localhost:27017',
     options:
      { servers: [Array],
        caseTranslate: true,
        useNewUrlParser: true,
        socketTimeoutMS: 360000,
        connectTimeoutMS: 30000,
        promiseLibrary: [Function: Promise] },
     promiseLibrary: [Function: Promise],
     dbCache: {},
     sessions: [] },
  topology:
   Server {
     domain: null,
     _events:
      { serverOpening: [Function],
        serverDescriptionChanged: [Function],
        serverHeartbeatStarted: [Function],
        serverHeartbeatSucceeded: [Function],
        serverHeartbeatFailed: [Function],
        serverClosed: [Function],
        topologyOpening: [Function],
        topologyClosed: [Function],
        topologyDescriptionChanged: [Function],
        commandStarted: [Function],
        commandSucceeded: [Function],
        commandFailed: [Function],
        joined: [Function],
        left: [Function],
        ping: [Function],
        ha: [Function],
        authenticated: [Function],
        error: [Function],
        timeout: [Function],
        close: [Function],
        parseError: [Function],
        open: [Object],
        fullsetup: [Object],
        all: [Object],
        reconnect: [Function] },
     _eventsCount: 25,
     _maxListeners: Infinity,
     clientInfo:
      { driver: [Object],
        os: [Object],
        platform: 'Node.js v8.9.3, LE' },
     s:
      { coreTopology: [Object],
        sCapabilities: null,
        clonedOptions: [Object],
        reconnect: true,
        emitError: true,
        poolSize: 5,
        storeOptions: [Object],
        store: [Object],
        host: 'localhost',
        port: 27017,
        options: [Object],
        sessionPool: [Object],
        sessions: [],
        promiseLibrary: [Function: Promise] } } }
3
can we see the console output from your application?Jim B.
Doesn't seems a problem with the code, Are you trying to connect to remote mongodb hosted on another server?Suresh Prajapati
Add a line with console.log(db) after this line: github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/… and share the output pleaseshkaper
@shkaper I added the outputFriso
@SureshPrajapati I'm trying to connect to localhost, I added the connection string I'm usingFriso

3 Answers

7
votes

The issue is because mongo.connect is async code and controller call it wrongly. so the subsequent line mongo.getDb() will be executed without having this.client initiated properly.

Since you use Typescript, we can use async/await for cleaner code.

async connect() { // add async
    console.log('connecting to mongo');

    try {
      if (!this.client) { // I added this extra check
        console.log('setting client');
        this.client = await MongoClient.connect(this.connectionString, { useNewUrlParser: true })
        console.log(this.client);      
      }
    } catch(error) {
      console.log('error during connecting to mongo: ');
      console.error(error);
    }    
}

And in controller code

async get(req: Request, res: Response) { // add async
  const mongo = new MongoDb();
  await mongo.connect(); // add await
  const db = mongo.db(); 
  // ....     
}

I tried your repo with the changes and got this response

enter image description here

Hope it helps

1
votes

Based on what you said about neither error nor success callbacks being called, I think the problem is not only in code here: https://github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/db/mongo.db.ts But also e.g. here: https://github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/controllers/to-do.controller.ts When you run mongo.connect there should be an option to wait for the promise returned from MongoClient.connect. So I would change code in mongo.db.ts to for example sth like this (it depends how you want to handle this promise):

connect() {
    console.log('connecting to mongo')
    return MongoClient.connect(this.connectionString, {useNewUrlParser: true})
    .then(client => {
        console.log('setting client');
        this.client = client;
        console.log(this.client);
    })
    .catch(error => {
        console.log('error during connecting to mongo: ');
        console.error(error);
    });
}

Then in to-do.controller.ts you can await this or use then:

get(req: Request, res: Response) {
    const mongo = new MongoDb();
    mongo.connect().then(() => {

      const db = mongo.getDb();
      const collection = db.collection('todo', (error, collection) => {
          if (error) {
              res.json(error);
              res.statusCode = HttpStatus.BAD_REQUEST
              return;
          }

          collection.find().toArray((error, result) => {
              if (error) {
                  res.json(error);
                  res.statusCode = HttpStatus.BAD_REQUEST
              }

              res.json(result);
              res.statusCode = HttpStatus.OK;
          })

      });

      mongo.close();
    });
} 

I suppose that your code just does not "wait" for connection to be established and then fails.

1
votes

The above solutions do work but there is another method that I like a bit better. It more resembles a standard HTTP call (I've gotten it to work with and without the async-await, but it's a good idea to leave it in):

private async connect(){
    this.mongo = new mongodb.MongoClient(this.url, {useNewParser: true});
    await this.mongo.connect((err, client) => {
        if(client.isConnected()){
            console.log('setting client');
            this.client = client;
            console.log(this.client);
        }
        else{
            console.log('error during connecting to mongo: ');
            console.error(error);
        }
    });
}

This is based off the Node.js driver api v3.2 that you can look over here: https://mongodb.github.io/node-mongodb-native/3.2/api/index.html

It might just be a different way of shaving a cat but I figure it's worth sharing for anyone else who ends up here later.