How to make watch() function work in mongoDB? - node.js

I am trying to build a Chat-App using Pusher for realtime communication with the help of MongoDB. These are my files.
dbMessages.js
import mongoose from 'mongoose'
const pinguSchema = mongoose.Schema({
message: String,
name: String,
timestamp: String,
received: Boolean
})
export default mongoose.model('messagecontents', pinguSchema)
server.js
import express from "express"
import mongoose from 'mongoose'
import Messages from './dbMessages.js'
import Pusher from 'pusher'
//config
const app = express()
const port = process.env.PORT || 9000
const pusher = new Pusher({
appId: "###",
key: "###",
secret: "###",
cluster: "###",
useTLS: ###
});
//middleware
app.use(express.json())
//DB config
const connection_url = "###";
mongoose.connect(connection_url,{
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true
})
const db = mongoose.connection;
db.once("open", () => {
console.log("DB connected...")
})
const msgCollection = db.collection("messagecontents");
const changeStream = msgCollection.watch();
changeStream.on("change", (change)=>{
console.log(change);
//api routes
app.get('/', (req,res) => {
res.status(200).send('hello')
})
app.get('/messages/sync', (req,res) => {
Messages.find((err, data) => {
if(err){
res.status(500).send(err)
} else{
res.status(200).send(data)
}
})
})
app.post('/messages/new', (req,res) => {
const dbMessage = req.body
Messages.create(dbMessage, (err, data) => {
if(err){
res.status(500).send(err)
} else{
res.status(201).send(data)
}
})
})
//listen
app.listen(port, () => console.log(` Server running on port: ${port}`))
What i am trying to get is the 'change' in my console, By the API http://localhost:9000/messages/new But The Error that I am getting is
[nodemon] 2.0.4 [nodemon] to restart at any time, enter rs [nodemon]
watching path(s): . [nodemon] watching extensions: js,mjs,json
[nodemon] starting node server.js C:\Users\Desktop\Pingu - Chat
App\backend\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:145
throw new Error('Collection method ' + i + ' is synchronous');
^
Error: Collection method watch is synchronous
at NativeCollection. [as watch] (C:\Users\Desktop\Pingu - Chat
App\backend\node_modules\mongoose\lib\drivers\node-mongodb-native\collection.js:145:15)
at file:///C:/Users/Desktop/Pingu%20-%20Chat%20App/backend/server.js:38:36
at ModuleJob.run (internal/modules/esm/module_job.js:146:23)
at async Loader.import (internal/modules/esm/loader.js:165:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5) [nodemon] app crashed - waiting for file changes before starting...
Here is the link to the documentation docs.mongodb.com/manual/changeStreams from here what I can get is that it can be done in two ways I implemented the 'watch' way, but don't know how to implement the async-await way.
Also here is the link to the youtube video from where I was trying to learn, youtube.com/watch?v=gzdQDxzW2Tw this part start from the time-stamp 2:59:00
Can anyone Help? Thanks in Advance.

I'm sorry that the comments that you got were completely unhelpful and a waste of time. All of the code that you shared is actually helpful in terms of fixing the problem.
The issue is that you are calling the watch function without ensuring that you have a connection to the database. Connecting to mongo is async, so when you call watch function, you might still be in the process of making a a db connection. Modify your code like so...
const db = mongoose.connection;
db.once("open", () => {
console.log("DB connected...");
const msgCollection = db.collection("messagecontents");
const changeStream = msgCollection.watch();
changeStream.on("change", (change) =>
console.log(change);
);
})
I hope that helps. I am very disappointed in the other comments received.

Related

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: https://www.mongodb.com/languages/mern-stack-tutorial
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.
https://media.discordapp.net/attachments/1072903958386983022/1072903958974189619/image.png
// 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.
https://media.discordapp.net/attachments/1072903958386983022/1072903959297146980/image.png?width=1017&height=389
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;
},
};
connectToServer is asynchronous. That means that the code will continue to execute without waiting for this function to complete.
This function is where the _db value is set. Because of the way your code is executing, you're attempting to access the _db value via getDB() before it has been set. As a result, you get back an undefined value.
You'll need to await the connectToServer function, or provide a fallback inside of getDB to handle the scenario where the DB has not yet been initialized.
Thanks to Jake Haller-Roby 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);
}
});

Failed to load resource: net::ERR_CONNECTION_REFUSED socket.io

I am building a simple chat application using Socket.io, Node.js, and React.js.
The server on the backend connects fine at first.
However when trying to make requests to the server from my front end the server keeps on crashing. Anyone understand why?
Here is the error..
[nodemon] 2.0.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node index.js`
server started on port 4000
new connection
events.js:109
throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener);
^
TypeError [ERR_INVALID_ARG_TYPE]: The "listener" argument must be of type function. Received undefined
at checkListener (events.js:109:11)
at _addListener (events.js:344:3)
at Socket.addListener (events.js:402:10)
at Namespace.<anonymous> (/Users/macbook/Projects/chat-react/server/index.js:16:10)
at Namespace.emit (events.js:311:20)
at Namespace.emit (/Users/macbook/Projects/chat-react/server/node_modules/socket.io/lib/namespace.js:213:10)
at /Users/macbook/Projects/chat-react/server/node_modules/socket.io/lib/namespace.js:181:14
at processTicksAndRejections (internal/process/task_queues.js:79:11) {
code: 'ERR_INVALID_ARG_TYPE'
}
[nodemon] app crashed - waiting for file changes before starting...
currently my server looks like this
index.js
const express = require ('express');
const socketio = require('socket.io');
const http = require('http');
const PORT = process.env.PORT || 4000;
const router = require('./router');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
io.on('connection', (socket) => {
console.log('new connection');
socket.on('disconnect'), () => {
console.log('User has left!!');
}
});
app.use(router);
server.listen(PORT, () => console.log(`server started on port ${PORT}`));
and my client looks like this
Chat.js
import React, { useState, useEffect } from 'react';
import quertString from 'query-string';
import io from 'socket.io-client';
let socket;
const Chat = ({ location }) => {
const [name, setUserName] = useState('');
const [room, setRoomID] = useState('');
const ENDPOINT = 'localhost:4000';
useEffect(() => {
const { name, room } = quertString.parse(location.search);
socket = io(ENDPOINT);
setUserName(name);
setRoomID(room);
console.log(socket)
});
return (
<h1>Chat</h1>
)
};
export default Chat;
any help would be great.
You have an error with your code. Look here:
socket.on('disconnect'), () => {
console.log('User has left!!');
}
Notice something? Yes, the function you wanted to call is not in the parentheses. Even though that wouldn't cause a syntax error, you probably didn't want to do that. Instead, you need to put the function inside the parentheses, like so:
socket.on('disconnect', () => {
console.log('User has left!!');
});
Note: to learn more about the comma operator, refer to these sites:
https://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator.
JS statement separator with commas

Getting Error 'DB' is not defined when trying to invoke MongoDB with nodeJS

I am totally absolutely novice to MongoDB and this is my first ever venture. I am still scraping around for basic ideas and procedures. Trying to build just a basic sample application with React and MongoDB as backend. I am trying to start the backend part of my project with nodemon server and getting error message on console.
The error that I am getting is -->
module.exports = mongoose.model('playerDB', playerDB);
^
ReferenceError: playerDB is not defined
at Object.<anonymous> (E:\REACT\MyProj\backend\server.js:46:45)
at Module._compile (internal/modules/cjs/loader.js:776:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
[nodemon] app crashed - waiting for file changes before starting...
I do not know what I am doing honestly. Just trying random things. When I start mongo.exe and run
use playerDB
it gives me message,
switched to db playerDB
So what's the trouble? What is wrong?
My server.js code is as such -
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const PORT = 4000;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const playerRoutes = express.Router();
let Player = require('./player.model');
app.use(cors());
app.use(bodyParser.json());
mongoose.connect('mongodb://127.0.0.1:27017/playerDB', { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', function () {
console.log("MongoDB database connection established successfully");
});
playerRoutes.route('/').get(function (req, res) {
Player.find(function (err, todos) {
if (err) {
console.log(err);
} else {
res.json(todos);
}
});
});
playerRoutes.route('/add').post(function (req, res) {
let player = new Player(req.body);
player.save()
.then(todo => {
res.status(200).json({ 'player': 'player added successfully' });
})
.catch(err => {
res.status(400).send('adding new player failed');
});
});
app.use('/playerDB', playerRoutes);
module.exports = mongoose.model('playerDB', playerDB);
app.listen(PORT, function () {
console.log("Server is running on Port: " + PORT);
});
So which part is wrong? Where is my mistake? When I run mongod.exe it gives me the ready message as
2019-09-15T00:41:03.305-0700 I SHARDING [LogicalSessionCacheReap] Marking collection config.transactions as collection version: <unsharded>
2019-09-15T00:41:03.306-0700 I NETWORK [initandlisten] waiting for connections on port 27017
**EDIT -> this is my player model class --> player.model.js,
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Player = new Schema({
player_name: {
type: String
},
player_description: {
type: String
},
player_position: {
type: String
},
player_age: {
type: String
},
player_club: {
type: String
},
player_: {
type: String
},
player_isactive: {
type: Boolean
},
player_completed: {
type: Boolean
}
});
Can someone direct me step by step, sequentially??
Thanks,

Unable to connect to MongoDB cloud by following code. I need to know whats wrong with this code?

I am unable to connect to cloud mongodb with the following code. Can anyone please tell me whats wrong with this code?
name: 'MongoNetworkError',
errorLabels: [ 'TransientTransactionError' ],
[Symbol(mongoErrorContextSymbol)]: {} }
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = express();
//body parser middleware
app.use(bodyParser.json());
//db config
const db = require('./config/keys').mongoURI;
//Connect to mongo
mongoose
.connect(db, { useNewUrlParser: true })
.then(() => console.log("MongoDB connected"))
.catch(err => console.log(err));
const port = process.env.PORT || 5000;
app.listen(port, () => console.log('server started on port ${port}'));
There are multiple steps you should follow to be able to connect Mongo DB so first make sure that you created an account plus connecting to a cluster, while creating it you'll be provided with enough info to create a cluster take your time and read.
after doing that the code is very simple:
const mongoose = require("mongoose");
mongoose.connect(
"mongodb+srv://[ACCOUNT NAME]:[PASSWORD]#cluster0-sxlgp.gcp.mongodb.net/test?retryWrites=true&w=majority", { useNewUrlParser: true }
);
replace ACCOUNTNAME and PASSWORD with info you provided when you created your MongoDB account
This can be found in their documentation try taking your time reading the documentation.
I believe your code looks good the error you are getting TransientTransactionError is temporary please use events to handle your connection result
mongoose
.connect(db, { useNewUrlParser: true })
mongooose.connection.once('open', () => {
console.log('db connection success');
});
mongooose.connection.on('err', (err) => {
console.log('db connection failed');
});

how to use the connection.db.collection function?

I have implemented the following code from this link:
What is best way to handle global connection of Mongodb in NodeJs
to create a class for the connection of MongoDB. But when I try to call the singleton class in the router, I get the following error:
TypeError: Connection.db.collection is not a function
mongodb.js
const MongoClient = require('mongodb').MongoClient
const url = '...';
class Connection {
static connectToDB() {
if ( this.database ) return Promise.resolve(this.database)
return MongoClient.connect(this.url, {useNewUrlParser: true}, (err, db) => {
if (err) console.log(err);
else {
this.db = db;
console.log('MongoDB connected');
}
})
}
}
Connection.db = null
Connection.url = url
Connection.options = {
bufferMaxEntries: 0,
reconnectTries: 5000,
}
module.exports = Connection;
app.js
const express = require('express');
const app = express();
let bodyParser = require('body-parser')
// mongodb config
const Connection = require('../config/mongodb');
const server = app.listen(3000, () => {
Connection.connectToDB(); // output: MongoDB connected
console.log(`listening to port: ${port} on http://127.0.0.1:3000}/`); // output: listening to port: 3000 on http://127.0.0.1:3000/
});
router.js
const router = require('express').Router();
let Connection = require('../config/mongodb');
router.post('/data', (req, res) => {
Connection.db.collection('tweets').find({}) // output: TypeError: Connection.db.collection is not a function
.then(tweets => console.log(tweets))
.catch(err => console.log(err));
});
Try once to package.json, change mongodb line to "mongodb": "^2.2.33". You will need to npm uninstall mongodb; then npm install to install this version.
The question you linked uses promises throughout, whereas you're using the callback version of connect.
return MongoClient.connect(this.url, {useNewUrlParser: true}, (err, db) => ...
You then call this without returning in your server:
Connection.connectToDB();
console.log(`listening to port: ${port} on http://127.0.0.1:3000}/`);
There is therefore no guarantee that your connection will have been made by the time your first api request comes in. In fact, if you did:
Connection.connectToDB();
console.log(`listening to port: ${port} on http://127.0.0.1:3000}/`);
Connection.db.collection('tweets').find({});
It would fail every time as Connection.db will still be null.
In the example you linked, using Promises protect against that. Note in particular the connect method:
static connectToDB() {
if ( this.database ) return Promise.resolve(this.database)
// ** USING THE PROMISE VERSION OF CONNECT **
return MongoClient.connect(this.url, this.options)
.then(db => this.db = db)
}
and your usage code should also use promises:
return Connection.connectToDB()
.then(() => {
// do something here
});

Resources