Solved: MERN Tutorial Issue: Cannot read properties of undefined (reading 'collection')

I am following the MongoDB MERN tutorial and when my front-end tries to connect to the DB to pull documents it errors out. I have pulled the official version of their GitHub repo and added my connection information and it works properly with theirs. The only differences I can find is theirs uses mongoose, which the tutorial doesn’t reference, and the versions of the packages are older.

Tutorial: How To Use MERN Stack: A Complete Guide | MongoDB
npm version: 9.4.1

Error

$ npm start

> server@1.0.0 start
> node server.js

Server is running on port: 5000
TypeError: Cannot read properties of undefined (reading 'collection')
    at D:\MERN\mine\server\routes\record.js:19:6
    at Layer.handle [as handle_request] (D:\MERN\mine\server\node_modules\express\lib\router\layer.js:95:5)
    at next (D:\MERN\mine\server\node_modules\express\lib\router\route.js:144:13)
    at Route.dispatch (D:\MERN\mine\server\node_modules\express\lib\router\route.js:114:3)
    at Layer.handle [as handle_request] (D:\MERN\mine\server\node_modules\express\lib\router\layer.js:95:5)
    at D:\MERN\mine\server\node_modules\express\lib\router\index.js:284:15
    at Function.process_params (D:\MERN\mine\server\node_modules\express\lib\router\index.js:346:12)
    at next (D:\MERN\mine\server\node_modules\express\lib\router\index.js:280:10)
    at Function.handle (D:\MERN\mine\server\node_modules\express\lib\router\index.js:175:3)
    at router (D:\MERN\mine\server\node_modules\express\lib\router\index.js:47:12)

See attached below image and code for line 19 of record.js.

// This section will help you get a list of all the records.
recordRoutes.route("/record").get(function (req, res) {
  let db_connect = dbo.getDb("employees");
  db_connect
    .collection("records")
    .find({})
    .toArray(function (err, result) {
      if (err) throw err;
      res.json(result);
    });
});

Image showing WinMerge comparison of my repo vs GitHub repo.


I know that my connection credentials are fine as I have used them with MongoDB Compass and their GitHub repo.
I have added numerous console.log commands in places to try and determine what is being set when the server runs.
Adding console.logs within the connectToServer anonymous function never triggers even though it should occur within server.js on line 14.

server.js

const express = require("express");
const app = express();
const cors = require("cors");
require("dotenv").config({ path: "./config.env" });
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use(require("./routes/record"));
// get driver connection
const dbo = require("./db/conn");

app.listen(port, () => {
  // perform a database connection when server starts
  dbo.connectToServer(function (err) {
    if (err) console.error(err);

  });
  console.log(`Server is running on port: ${port}`);
});

record.js - partial

const express = require("express");

// recordRoutes is an instance of the express router.
// We use it to define our routes.
// The router will be added as a middleware and will take control of requests starting with path /record.
const recordRoutes = express.Router();

// This will help us connect to the database
const dbo = require("../db/conn");

// This help convert the id from string to ObjectId for the _id.
const ObjectId = require("mongodb").ObjectId;


// This section will help you get a list of all the records.
recordRoutes.route("/record").get(function (req, res) {
  let db_connect = dbo.getDb("employees");
  db_connect
    .collection("records")
    .find({})
    .toArray(function (err, result) {
      if (err) throw err;
      res.json(result);
    });
});

Note: I did try installing mongoose npm install mongoose on the server and it didn’t change the results.

If I modify the conn.js file to use async and await I can get details from the db such as a count of records from employees collection. However, none of the routes work properly for the React frontend, though they don’t throw errors either.

Revamped conn.js


const { MongoClient } = require("mongodb");
const Db = process.env.ATLAS_URI;
const client = new MongoClient(Db, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

let _db;

module.exports = {
  connectToServer: async function (callback) {
    console.log("test");

    try {
      await client.connect();
    } catch (e) {
      console.error(e);
    }

    _db = client.db("employees");

    try {
      var count = await _db.collection("records").countDocuments();
      console.log(count);
    } catch (e) {
      console.error(e);
    }

    if(_db !== undefined){
      return true;
    }
  },
  getDb: function () {
    return _db;
  },
};
2 Likes

Thanks to Jake Haller-Roby on stackoverflow I was led down the right path. This had to do with async and await.

However, the GitHub repo from the tutorial doesn’t rely upon async and await and works fine. I am going to assume that some newer versions of mongodb or express with nodejs changes how things work.

Here is the code I ended up using.

server.js

const express = require("express");
const app = express();
const cors = require("cors");
require("dotenv").config({ path: "./config.env" });
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use(require("./routes/record"));
// get driver connection
const dbo = require("./db/conn");

app.listen(port, async () => {
  // perform a database connection when server starts
  await dbo.connectToServer(function (err) {
    if (err) console.error(err);
  });
  console.log(`Server is running on port: ${port}`);
});

conn.js


const { MongoClient } = require("mongodb");
const Db = process.env.ATLAS_URI;
const client = new MongoClient(Db, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

let _db;

module.exports = {
  connectToServer: async function (callback) {

    try {
      await client.connect();
    } catch (e) {
      console.error(e);
    }

    _db = client.db("employees");

    return (_db === undefined ? false : true);
  },
  getDb: function () {
    return _db;
  },
};

Within the recordRoutes route for getting the list of records I ran into an issue with toArray where it was never returning its promise. After googling for a bit I found there are multiple ways of handling this. Using .then after toArray works as well as storing the results from the toArray in a variable and using an await on its call. Below are the two examples.

.then

// This section will help you get a list of all the records.
recordRoutes.route("/record").get(async function (req, response) {
  let db_connect = dbo.getDb();

  db_connect
    .collection("records")
    .find({})
    .toArray()
    .then((data) => {
      console.log(data);
      response.json(data);
    });

});

try and await

// This section will help you get a list of all the records.
recordRoutes.route("/record").get(async function (req, response) {
  let db_connect = dbo.getDb();

  try {
    var records = await db_connect
      .collection("records")
      .find({})
      .toArray();
    response.json(records);
  } catch (e) {
    console.log("An error occurred pulling the records. " + e);
  }

});
3 Likes

Thank you so much! I’ve been looking for answers to this problem but was struggling to come up with anything. This is the first time I’ve seen the same problem documented anywhere.

4 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.