TypeError: Cannot read properties of undefined (reading 'insertOne')

Hi,Im currently learning backend, and I had this error when I try to add a review using the curl command: curl -X POST ://localhost:4444/api/v1/reviews/new -H “Content-Type:application/json” -d ‘{“movieId”:12,”user”:”sara”,”review”:”good”}’, this error appears to me:TypeError: Cannot read properties of undefined (reading ‘insertOne’), i have installed all the nessury models in package.json ,also these are the project files, i gusse there is a proplem connecting ReviewsDAO with the database.

reviewsDAO.js:

import mongodb from 'mongodb';
//so we can send and receive inputs using id 
const ObjectID=mongodb.ObjectId;

let reviews;

export default class ReviewsDAO{
    //conn => the connection from the movieDB file 
    static async injectDB(conn){
        //if there is a db connection don't do anything
        if(reviews){
            return
        }
        //if thee is not a db connection create a connection using try
        try{
            //this is from the mongoDb collections command we tried to accuses the reviews db then to the collection reviews
            reviews = await conn.db("reviews").collection("reviews");
        } catch(e){
            console.error(`Unable to establish collection handles in userDAO: ${e}..`);
        };
    };

    //to insert a review to a collection 
    static async addReview(movieId,user,review){
        try{
            // all of this is a key value 
            const reviewDoc={
                movieId:movieId,
                user:user,
                review:review
            }
            //we try to insert a doc to the collection reviews 
            return await reviews.insertOne(reviewDoc);
        }catch(e){
            console.error(`Unable to post review: ${e}`);
            return{error:e};
        };
    };

    //we pass the review id to get the review from the db 
    static async getReview(reviewId){
        try{
            return await reviews.findOne({_id:ObjectID(reviewId)})
        }catch(e){
            console.error(`Unable to get review: ${e}`);
            return{error:e};
        };
    };

    //to update the review
    static async updateReview(reviewId,user,review){
        try{
            //to update the review we first add the Id then update it by adding the review and the user 
            const updateResponse=await review.updateOne({_id:ObjectID(reviewId)}, 
            //$set is a command from mongoDb to update something on the db 
            {$set:{user:user,review:review}});
            return updateResponse;
        }catch(e){
            console.error(`Unable to update review: ${e}`);
            return{error:e};
        };

    };

    //get movies by id
    static async getReviewByMovieId(movieId){
        //if we want to find a movie we first add the movie id then store it into the cursor then convert the courser to an array
        try{
            //the movieId is a string value but we convert it by using parseInt function from javascript
            const cursor=await reviews.find({movieId: parseInt(movieId)});
            //convert the int value in cursor to an array
            return cursor.toArray();
           
        }catch(e){
            console.error(`Unable to get review: ${e}`);
            return{error:e};
        };
    };

};

reviews.controller.js:


//this file is to get info from the route then doing something with the info

import ReviewsDAO from "../dao/reviewsDAO.js";
//export the class reviewsController to other files
export default class ReviewsController{
    //we create apiPostReviews as static so we can call it directly from reviewsController without an instance in other files
    //we used async so we can waite for something to happened
    static async apiPostReview(req,res,next){
        try{
            //get the movie id and the review and the user 
            const movieId = parseInt(req.body.movieId)
            const review=req.body.review;
            const user=req.body.user;
            console.log('movieId', movieId)
            //wait for the respond
            const reviewResponse=await ReviewsDAO.addReview(
                movieId,
                user,
                review
            );
            //add the review to the json 
            res.json({status:'success'});
            //if there was an error this will appear
        }catch(e){
            res.status(500).json({error:e.message});
        };
    };

    //
    static async apiGetReviews(req,res,next){
        try{
            //to get the id of the review
            let id=req.params.id||{};
            //stor the id in review
            let review=await ReviewsDAO.getReview(id);
            //if there is no reviews
            if(!review){
                res.status(404).json({error:"Not Found"});
                return
            };
            //respond with the review
            res.json(review)
        }catch(e){
            console.log(`ipa ${e}`);
            res.status(500).json({error:e});
        };
    };

    //updating the review
    static async apiUpdateReview(req,res,next){
        try{
            const reviewId=req.params.id;
            const review=req.body.review;
            const user=req.body.user;

            const reviewResponse=await ReviewsDAO.UpdateReview(
                reviewId,
                review,
                user
            );
            
            var{error}=reviewResponse;
            //first way to say if there was an error
            if(error){
                res.status(500).json({error});
            };
            //second way
            if(reviewResponse.modifiedCount===0){
                throw new Error(
                    'Unable to update reviews'
                );
            };
            res.json({status:"success"});
        }catch(e){
            res.status(500).json({error:e.message});
        };
    };

    //delete the review
    static async apiDeleteReview(req,res,next){
        try{
            const reviewId=req.params.id;
            const reviewResponse = await ReviewsDAO.deleteReview(reviewId)
            res.json({status:"success"});   
            
        }catch(e){
            res.status(500).json({error:e.message});
        };
    
    };
    //to get all the reviews to a specific movie
    static async getReviewByMovie(req,res,next){
        try{
            let id = req.params.id ||{}
            let reviews= await ReviewsDAO.getReviewByMovie(id)
            if(!reviews){
                res.status(404).json({error:"Not Found"});
                return
            }
            res.json(reviews)
        }catch(e){
            console.log(`ipa ${e}`);
            res.status(500).json({error:e.message});
        };

    };
};

reviews.route.js:

import express from 'express';
import ReviewsController from "./reviews.controller.js";
// const router = require('router');
const router=express.Router();

// router.route("/").get((req,res)=>res.send("Hello world"));

router.route("/movie/:id").get(ReviewsController.apiGetReviews);
router.route("/new").post(ReviewsController.apiPostReview);
router.route("/:id")
    .get(ReviewsController.apiGetReviews)
    .put(ReviewsController.apiUpdateReview)
    .delete(ReviewsController.apiDeleteReview)

export default router;

moviesdb.js:


import app from './server.js';
import ReviewsDAO from "./dao/reviewsDAO.js";
import mongodb from 'mongodb';
import dotenv from 'dotenv';

const MongoClient=mongodb.MongoClient;

//Load the environment variables from the .env file
dotenv.config();
//Access the environment variables

const mongo_username = process.env.MONGO_USERNAME;
const mongo_password = process.env.MONGO_PASSWORD;

uri=`mongodb+srv://${mongo_username}:${mongo_password}@cluster0.b5f7prn.mongodb.net/test`;
const port=4444;

MongoClient.connect(
  uri,
{
  maxPoolSize: 50,
  wtimeoutMS: 2500,
  useNewUrlParser: true
})
.catch(err => {
    console.error(err.stack)
    console.error("Error connecting to the database:", err.message)
    process.exit(1)
  })
.then(async client => {
    await ReviewsDAO.injectDB(client)
    app.listen(port, () => {
      console.log(`listening on port ${port}`)
    })
  })
 

server.js:

import express from "express"
import cors from "cors"
import reviews from "./api/reviews.route.js"

const app = express()

app.use(cors())
app.use(express.json())

app.use("/api/v1/reviews", reviews)
app.use("*", (req, res) => res.status(404).json({error: "not found"}))

//to listen to the router 
app.listen( 4444, () => {
    console.log(`Listening on port 4444`);
    
  });

export default app

Just to be clear (a lot of people misunderstand this), that is saying that whatever you are trying to read “insterOne” off of is undefined. There is a strict rule in JS that you cannot access a property or method off something that is null or undefined.

I see this:

return await reviews.insertOne(reviewDoc);

So, it is saying that reviews is undefined.

When you declare it, it is undefined:

let reviews;

Are you sure it is getting initialized somewhere? Put in some log statements and figure out what is happening to it.

hi thanks for your help, i did assign values to reviews variable in injectDB function in ReviewsDAO calss to creat the db and collection before the addReview function. i declare reviews above the class becuse i want to acsses to the varibles in all the functions in ReviewsDAO calss and also when i import ReviewsDAO.js file in movesdb.js file… idk if im doing it right im just starting learning mongodb and nodejs, i added all the project files in the qustion.

    static async injectDB(conn){
        if(reviews){
            return
        }
        try{

> reviews = await conn.db("reviews").collection("reviews");

        } catch(e){
            console.error(`Unable to establish collection handles in userDAO: ${e}..`);
        };
    };

Why not make it a member of the class?

I would put log statements in everywhere that you change/access reviews and see what is happening when.

when i declare it above the “static async injectDB(conn)” its give me an error. I followd a YT tutorial and it works for them.
i think it did not get connected to the db becuse i added a document manually and whrn i try to get it by the id its give me an error too: TypeError: Cannot read properties of undefined (reading ‘findOne’)

You should be able to put that value on the class - this is where it should be. We’d have to see the code to deal with the errors.

You get the error you gave, but what is the value of the thing that you are trying to access that method on? Trace through the code, find out. Find out why it doesn’t get loaded or initialized. Or, if it can be uninitialized, deal with that.

I’m following the same book as you.

In index.js, you need to put

" await ReviewsDAO.injectDB(client);"

In main() , this initialises the reviews variable.

I’m guessing the author forgot to mention this

Correction: The line is in the book. I must have missed it