Hazzaldo
Hazzaldo

Reputation: 563

Exporting Mongoose methods in Node.js (TypeError: Cannot read property 'name' of null)

I've created a simple todo checklist website, where users can view "Today's" todo lists (on Home page), add new items to the list, create and delete other todo lists. All my code worked perfectly. So I decided to refactor the code to separate the Mongoose (MongoDB) code into a separate module called model.js, from the app.js file (where the HTTP methods are handled).

Now, my Home page still loads on the browser, however, Nodemon shows that the app crashes in the terminal. If I select any option on the website, the page just doesn't display anything. So since my app is crashing on the Home route, I've placed console.log statements within app.get('/' ... api, and any methods it calls from the model.js file, in order to try to debug the issue, but to no avail.

Before I share my code, just to note that I only shared the app.get('/' ... and app.post('/'... code from app.js file, since the app is crashing on the Home page, and all of the code from the model.js file.

Here's my code: app.js

const express = require('express');
const bodyParser = require('body-parser');
const date = require(`${__dirname}\\date.js`);
const model = require(`${__dirname}\\model.js`); 
const ejs = require('ejs');
const _ = require('lodash');

const app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
app.use(express.static('public'));


//*** HTTP requests' handlers

app.get('/', (req, res) => {
  const defaultListName = "Today's";
  const todayFormatted = date.getDate();
  model.DBUtility.findOneList(defaultListName, (foundList) => {
    console.log('inside model.DBUtility.findOneList');
    if (!foundList) {
      console.log('inside !foundList. Just before createNewListInDB');
      model.DBUtility.createNewListInDB(defaultListName);
      res.redirect('/');
    } else { 
      res.render('list', {
        listTitlePassToH1: foundList.name,
        todayDate: todayFormatted,
        listItems: foundList.items
      });
      console.log(`else render list. found list: ${foundList.name}`);
    }
    console.log('end of model.DBUtility.findOneList')
  });
  console.log(`end of app.get('/'`);
});

app.post('/', (req, res) => {
  const receivedItem = req.body.newItem;
  const listName = req.body.listName;
  model.DBUtility.addNewItemToListInDB(listName, receivedItem);
  if (listName === "Today's") {
    res.redirect('/');
  } else {
    res.redirect('/' + listName);
  } 
});

const port = 3000;
app.listen(port, () => {
  console.log(`Server started, listening on port: ${port}`);
});

model.js:

const mongoose = require('mongoose');

//create DB
mongoose.connect('mongodb://localhost:27017/todolistDB', {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });


//create schema
const itemsSchema = new mongoose.Schema ({
  name: String
});

const listSchema = new mongoose.Schema ({
  name: String,
  items: [itemsSchema]
});

const Item = mongoose.model('Item', itemsSchema);
const List = mongoose.model('List', listSchema);


class DBUtility {
    constructor() {
        console.log("Constructor class DBUtility");
    }

    // *** DB utility methods

    static findOneList(listName, callBack) {

        List.findOne({name: listName}, (err, foundList) => {
            if (err) {
                console.log(err);
            } else {
                console.log(`Found list: ${foundList.name}`);
                var list = foundList; 
            } 
            callBack(list); 
        });
    }

    static findAllLists(callBack) {
        List.find({}, (err, allLists) => {
            if (err) {
                console.log(err);
            } else {
                console.log(`Found all lists: ${allLists}`);
                callBack(allLists);
            }  
        });
    }


    static createNewListInDB(listName) {
        //create default items for new list
        const introItem = new Item({
            name: 'Welcome to your todolist!'
        });

        const addItem = new Item({
            name: 'Hit the + button to add a new item.'
        });

        const deleteItem = new Item({
            name: '<-- Hit this to delete an item.'
        });

        const listNameCap = _.capitalize(listName);
        const list = new List({
            name: listNameCap,
            items: [introItem, addItem, deleteItem]
        });
        list.save(err => {
            if (err) return handleError(err);
            console.log(`List ${list} added successfully to DB.`);
        });
    }

    static addNewItemToListInDB(listName, itemName) {
        console.log(`List passed: ${listName}`);
        List.findOne({name: listName}, (err, foundList) => {
            if (err) {
                console.log(err);
            } else {
                foundList.items.push({ name: itemName });
                foundList.save(err => {
                if (err) return handleError(err);
                console.log(`Item ${itemName} added to list ${listName} successfully.`);
                });
            }  
        });  
    }

    static deleteItemFromListInDB(listName, itemID) {
        // findOneAndUpdate is a mongoose method, but $pull is a mongoDB method
        List.findOneAndUpdate({name: listName}, {$pull: {items: {_id: itemID}}}, (err, updatedList) => {
                if (err) {
                    console.log(err);
                } else {
                    console.log(`deleted item "${itemID}" from list ${updatedList} successfully.`);
                    console.log(`Updated list: ${updatedList}.`);
                }
        });
    }

    static deleteOneListFromDB(listID) {
        List.findByIdAndDelete(listID, (err, deletedList) => {
        if (err) {
            console.log(err);
        } else {
            console.log(`Deleted list ${deletedList.name} successfully`);
        }
        });
    }
}

exports.DBUtility = DBUtility;

Here's the logs and error I get in Terminal:

Server started, listening on port: 3000 
end of app.get('/' 
Found list: Today's 
inside model.DBUtility.findOneList 
else render list. found list: Today's 
end of model.DBUtility.findOneList
events.js:288 
      throw er; // Unhandled 'error' event
      ^

TypeError: Cannot read property 'name' of null

Note, I have only one LIST object stored in MongoDB database, which is "Today's" todo list:

db.lists.find().pretty() 
{
        "_id" : ObjectId("5e98982a7f8b083a6cf2d433"),
        "name" : "Today's",
        "items" : [
                {
                        "_id" : ObjectId("5e98982a7f8b083a6cf2d430"),
                        "name" : "Welcome to your todolist!"
                },
                {
                        "_id" : ObjectId("5e98982a7f8b083a6cf2d431"),
                        "name" : "Hit the + button to add a new item."
                },
                {
                        "_id" : ObjectId("5e98982a7f8b083a6cf2d432"),
                        "name" : "<-- Hit this to delete an item."
                }
        ],
        "__v" : 0
}

When I deleted this object from MongoDB terminal using:

> db.lists.deleteOne( {name: "Today's"} ) 
{ "acknowledged" : true, "deletedCount" : 1 }

Now even my Home page doesn't load anymore. I get:

This site can’t be reached

And the error in Terminal is the same, however, I'm only seeing the very last console.log statement in app.get('/'...

Server started, listening on port: 3000 
end of app.get('/' 
events.js:288 
      throw er; // Unhandled 'error' event
      ^

TypeError: Cannot read property 'name' of null

Also, model.js and app.js are on the same directory level:

enter image description here

Sorry for the long-winded post, but I wanted to make sure I'm thorough in my explanation.

EDIT Here's the full error message :

[nodemon] restarting due to changes...
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
Server started, listening on port: 3000
end of app.get('/'
events.js:288
      throw er; // Unhandled 'error' event
      ^

TypeError: Cannot read property 'name' of null
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\model.js:36:54
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4849:16
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4849:16
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4872:21
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\query.js:4379:11
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\kareem\index.js:135:16
    at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted 'error' event on Function instance at:
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\model.js:4851:13
    at C:\Users\hazzaldo\Desktop\...\todolist-v2\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
    [... lines matching original stack trace ...]
    at processTicksAndRejections (internal/process/task_queues.js:79:11)
[nodemon] app crashed - waiting for file changes before starting...

Upvotes: 0

Views: 898

Answers (2)

J&#243;zef Podlecki
J&#243;zef Podlecki

Reputation: 11283

Error

TypeError: Cannot read property 'name' of null

happens when you try to access 'name' property of a null object.

So the only candidates where error appears follow obj.name pattern.

Now when I think about it, another case would be name property destructuring but it if natively supported it gives another error.

Also

console.log('inside !foundList. Just before createNewListInDB');
model.DBUtility.createNewListInDB(defaultListName);
res.redirect('/');

function model.DBUtility.createNewListInDB(defaultListName); is asynchronous and after it you call res.redirect('/'). I don't know how Node Express handles this function internally but you should redirect after tasks were created.

Like

model.DBUtility.createNewListInDB(defaultListName)
.then(_ => {
  res.redirect('/');
});

--Edit

list variable wasn't declared outside if statement and could raise exception list is not defined

    static findOneList(listName, callBack) {
        var list = null;

        List.findOne({name: listName}, (err, foundList) => {
            if (err) {
                console.log(err);
            } else {
                console.log(`Found list: ${foundList.name}`);
                list = foundList; 
            } 
            callBack(list); 
        });
    }

Also, const _ = require("lodash"); was not included inside model.js. As a lodash method was used inside: static createNewListInDB(listName) method. Line: const listNameCap = _.capitalize(listName);

Upvotes: 1

Harshal Yeole
Harshal Yeole

Reputation: 4983

You are getting error on line no 36 of model.js

console.log(`Found list: ${foundList.name}`)

you can update your code like

static findOneList(listName, callBack) {

        List.findOne({name: listName}, (err, foundList) => {
            if (err || !foundList) {
                console.log(err);
            } else {
                console.log(`Found list: ${foundList.name}`);
                var list = foundList; 
            } 
            callBack(list); 
        });
    }

Upvotes: 0

Related Questions