Ryan Griggs
Ryan Griggs

Reputation: 2758

How to connect from NodeJS to MongoDB using Unified Topology and Promises

I'm new to MongoDB - sorry in advance for any pebkac.

Per this article, the Unified Topology paradigm moves away from the concept of "connecting" and instead simply sets up a connection string and begins doing operations. Each operation then fails or succeeds depending on whether the driver can reach a server at the time that operation is executed.

I'm trying to implement the following example code to establish a "connection" to a local MongoDB server on my test machine, but it's not working: Node is throwing uncaught Promise Reject messages, apparently because it's erroring out somewhere along the way.

However, the example code does not show how to implement the new paradigm using Promises. I'm assuming that's the main problem, so I tried the second code shown below, and am still not getting an object that allows me to retrieve records. Can you advise what I'm doing wrong, and also the recommended/best way to do this within an Express app to ensure it is as stable as possible?

First attempt: index.js:

async function init(){
    console.log("Starting...");
    const MongoClient = require('mongodb');
    const client = MongoClient('mongodb://test:test@localhost:27017', { useUnifiedTopology: true });
    const coll = client.db('test').collection('foo');
    await coll.insert({ test: 'document' });
    const docs = coll.find({ test: 1 }, { readPreference: 'secondary' }).toArray();
    console.dir({ docs });
    await client.close();
}


init();

Errors:

Starting...
(node:15328) UnhandledPromiseRejectionWarning: TypeError: client.db is not a function
    at init (index.js:5:22)
    at Object.<anonymous> (index.js:13:1)
    at Module._compile (internal/modules/cjs/loader.js:1128:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
    at Module.load (internal/modules/cjs/loader.js:983:32)
    at Function.Module._load (internal/modules/cjs/loader.js:891:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47
(node:15328) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:15328) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

So obviously this is trying to use Promises, but the example doesn't implement in this way.

Here's the second attempt:

index.js:

async function init(){
    console.log("Starting...");
    const MongoClient = require('mongodb');
    const client = MongoClient('mongodb://test:test@localhost:27017', { useUnifiedTopology: true })
        .then(async (result) => {
            console.log("Connected: result = ", result);

            const db = result('test');  // Get the 'test' database
            const coll = db.collection('foo');  // Ge the 'foo' collection
            await coll.insert( { test: 'document' });   // Create a new document
            const docs = coll.find({}, { readPreference: 'secondary'})  // Retrieve all docs
                .then((results) => {
                    const data = results.toArray();
                    console.log('Results: ', data);
                });
            await client.close();

        })
        .catch((err) => {
            console.log('Caught error: ', err);
        });
}


init();

Output:

Starting...
Connected: result =  MongoClient {
  _events: [Object: null prototype] { newListener: [Function (anonymous)] },
  _eventsCount: 1,
  _maxListeners: undefined,
  s: {
    url: 'mongodb://test:test@localhost:27017',
    options: {
      servers: [Array],
      caseTranslate: true,
      useUnifiedTopology: true,
      checkServerIdentity: true,
      sslValidate: true,
      auth: [Object],
      authSource: 'admin',
      dbName: 'test',
      socketTimeoutMS: 360000,
      connectTimeoutMS: 30000,
      retryWrites: true,
      useRecoveryToken: true,
      readPreference: [ReadPreference],
      credentials: [MongoCredentials],
      promiseLibrary: [Function: Promise]
    },
    promiseLibrary: [Function: Promise],
    dbCache: Map {},
    sessions: Set {},
    writeConcern: undefined,
    namespace: MongoDBNamespace { db: 'admin', collection: undefined }
  },
  topology: NativeTopology {
    _events: [Object: null prototype] {
      authenticated: [Function (anonymous)],
      error: [Function (anonymous)],
      timeout: [Function (anonymous)],
      close: [Function (anonymous)],
      parseError: [Function (anonymous)],
      fullsetup: [Function],
      all: [Function],
      reconnect: [Function (anonymous)],
      serverOpening: [Function (anonymous)],
      serverDescriptionChanged: [Function (anonymous)],
      serverHeartbeatStarted: [Function (anonymous)],
      serverHeartbeatSucceeded: [Function (anonymous)],
      serverHeartbeatFailed: [Function (anonymous)],
      serverClosed: [Function (anonymous)],
      topologyOpening: [Function (anonymous)],
      topologyClosed: [Function (anonymous)],
      topologyDescriptionChanged: [Function (anonymous)],
      commandStarted: [Function (anonymous)],
      commandSucceeded: [Function (anonymous)],
      commandFailed: [Function (anonymous)],
      joined: [Function (anonymous)],
      left: [Function (anonymous)],
      ping: [Function (anonymous)],
      ha: [Function (anonymous)]
    },
    _eventsCount: 24,
    _maxListeners: Infinity,
    s: {
      id: 0,
      options: [Object],
      seedlist: [Array],
      state: 'connected',
      description: [TopologyDescription],
      serverSelectionTimeoutMS: 30000,
      heartbeatFrequencyMS: 10000,
      minHeartbeatFrequencyMS: 500,
      Cursor: [Function: Cursor],
      bson: BSON {},
      servers: [Map],
      sessionPool: [ServerSessionPool],
      sessions: Set {},
      promiseLibrary: [Function: Promise],
      credentials: [MongoCredentials],
      clusterTime: null,
      iterationTimers: Set {},
      connectionTimers: Set {},
      clientInfo: [Object]
    }
  }
}

Caught error:  TypeError: result is not a function
    at index.js:8:15
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

Upvotes: 2

Views: 8366

Answers (1)

Ryan Griggs
Ryan Griggs

Reputation: 2758

Here's a full example of connecting to the MongoDB database from Node.JS using the Unified Topology paradigm (no explicit connect commands). Thanks to @MaheshBhatnagar for pointing out my mistake.

This example uses Promises, and after setting up the connection, begins inserting records at a frequency of one every 5 seconds. If you disable or disconnect the MongoDB database, it will cache the inserts until either (a. it times out, default of 30 seconds, or b. the connection is restored). If it times out, the inserts will be lost, but if the server is brought back online, subsequent requests will be handled properly.

Let me know if I am doing this wrong, or any suggestions.

index.js:

const url = 'mongodb://test:test@localhost:27017';


async function init(){
    console.log("init...");
    const MongoClient = require('mongodb');

    return MongoClient(url, { useUnifiedTopology: true}); // Return a Promise
}


async function go(db_result) {
    // Do an insert and output the last-inserted document.
    console.log("Go...");

    const db = db_result.db('test');  // Get the 'test' database
    const coll = db.collection('foo');  // Ge the 'foo' collection
    // Create a new document:
    await coll.insertOne({ test: 'document' }, (err, response) => {
        if (err) {
            console.log("Error inserting", err);
        }
        else
        {
            console.log("New record: ", response.ops[0])  // Output newly inserted record (thanks to https://stackoverflow.com/questions/40766654)
        }
    });   


}

// Sleep for a specified time:
function sleep(ms) {
    return new Promise( resolve => {
        setTimeout(resolve,ms)
    })
}


init()  // Set up connection, then start doing inserts.
    .then( async (db_result) => {
        // When MongoClient is instantiated:
        while(true) {
            console.log("Going...")
            go(db_result)
            console.log("Sleeping")
            await sleep(5000);
        }
    })
    .catch((err) => {
        console.log("Caught error: ", err)
    })

Upvotes: 1

Related Questions