I am getting started with mongoDB and I have to say that the official documentation is not that great to see how to implement it with nodejs.
I don't really know how to structure my server file to add mongoClient.connect, should my whole server be written inbeetwen the mongoClient.connect function in order to have access to the db, like in this boilerplate? I am using nodeJS/express.
If you know any good boilerplate, or anything, that could show me the structure of a backend with an implementation of mongoDB, I would really appreciate it. Every time I find something about mongoDB, it is actually about mongooooose!!
After further reasearch, here is what I was looking for, for those who wonder like me how to implement MongoDB (and not mongoose) with Express:
var express = require('express');
var mongodb = require('mongodb');
var app = express();
var MongoClient = require('mongodb').MongoClient;
var db;
// Initialize connection once
MongoClient.connect("mongodb://localhost:27017/integration_test", function(err, database) {
if(err) throw err;
db = database;
// Start the application after the database connection is ready
app.listen(3000);
console.log("Listening on port 3000");
});
// Reuse database object in request handlers
app.get("/", function(req, res) {
db.collection("replicaset_mongo_client_collection").find({}, function(err, docs) {
docs.each(function(err, doc) {
if(doc) {
console.log(doc);
}
else {
res.end();
}
});
});
});
I've found several ways of doing it, even in mongoDB's official pages.
By far, I prefer this one (not mine, source below) where you instantiate the connection in one file and export it and the database/client to the server file where express is instantiated:
(I copied only what's important, without error handling)
// database.js
const MongoClient = require('mongodb').MongoClient;
let _db; //'_' private
const mongoConnect = function(callback) {
MongoClient.connect(
'mongodb://localhost:27017',
{ useUnifiedTopology: true }
)
.then(client => {
_db = client.db('onlineshopping');
callback();
})
.catch(error => {
console.log(err);
throw new Error('DB connection failed...');
});
}
const getDB = () => {
if (_db) {
return _db;
} else {
throw new Error('DB connect failed');
}
}
exports.mongoConnect = mongoConnect;
exports.getDB = getDB;
// index.js
const express = require('express');
const app = express();
const mongoConnect = require('./util/database').mongoConnect;
// ...
mongoConnect(() => {
app.listen(3000);
})
Source:
https://github.com/TinaXing2012/nodejs_examples/blob/master/day9/util/database.js
Corresponding to this YouTube course that I recommend in this topic: https://www.youtube.com/watch?v=hh-gK0_HLEY&list=PLGTrAf5-F1YLBTY1mToc_qyOiZizcG_LJ&index=98
Other alternatives from mongoDB official repos, are:
https://github.com/mongodb-developer/mern-stack-example
https://github.com/mongodb-developer/nodejs-quickstart
Related
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);
}
});
I am using using NodeJS and MongoDb as a backend service in my android application.I want to know how can I pool connections so that it minimize the load on server and make fast operations and how to close the connection
to database after performing operation.
This is what I have been done so far:
const express = require('express');
const bodyParser = require('body-parser');
const env = require('dotenv').config();
const MongoClient = require('mongodb').MongoClient;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.post('/add', (req,res) => {
var data = {
User_id: req.body.userId,
Email:req.body.email,
Name: req.body.name,
Book_name: req.body.bookName,
};
MongoClient.connect(dburl, {useNewUrlParser:true} ,(err,client) => {
if(err){
console.log("Error".red, +err);
}
else{
var collect = client.db('Mydb').collection('Books');
collect.insertOne(data, (err,resp) =>{
if(err){
console.log("Error", +err);
}
else{
console.log("Successfully inserted");
}
client.close();
});
}
});
});
app.listen(port,() => {
console.log("App is running on:" +port);
});
Someone please let me know what else need to be added in above code to achieve desired results.Any help would be appreciated.
THANKS
MongoClient by default sets up a connection pool of size 5. You can initiliaze the connection and reuse it.
let connection;
MongoClient.connect(dburl, {useNewUrlParser:true} ,(err,client) => {
if(err){
console.log("Error".red, +err);
}
connection = client;
// maybe move your app.listen here to make sure server is started after connection is acquired or something equivalent
})
// elsewhere after connection is established:
connection.db('Mydb').collection('Books');
To increase/decrease the pool size you can pass the poolSize option with the required number.
i have a nodejs server which is getting list of a collection from mongodb . Here is its code . since am new to sockets so ..
const express = require("express");
const app = express();
const http = require("http").Server(app);
const socketio = require('socket.io');
after that iam simply getting data in a route . and one thing more all code is in one file and i do need express route as there are other routes in app. here is the mongodb code for getting list
app.post("/getAllOfferManagement",
async (req, res) => {
try {
MongoClient.connect(url,
function(err, db) {
if (err) throw err;
var dbo = db.db("realtime");
dbo
.collection("offer")
.find({})
.toArray(function(err,
result) {
if (err) throw err;
// console.log('getting it ');
res.send(result);
db.close();
});
});
} catch (err) {
res.send("error");
}
}); // its all working fine when i hit the route
http.listen(5000, function() {
console.log("Server Started!");
});
//serversidecode ends here
Now am getting the data through angular and here is the code for it
$scope.getAllOffer = function() {
$scope.mongoloader = true;
//nodejs api endpoint
$http.post("http://localhost:5000/getAllOffer").then(function(res) {
$scope.offersArray = res.data;
console.log('data here', res.data);
});
};
the above works fine . but i need to get data in realtime e.g when somone insert new doc in mongodb the the view get updates . am new to sockets so any help is appreciated. Thanks
For this u have to add an event to backend and as well as in frontend
Backend
io.on('connection', (socket) => {
console.log(socket.id);
socket.on('SEND_TITLE', function(data){
io.emit('RECEIVE_TITLE', data);
console.log('data',data)
})
});
For frontend u have to use socket io client module
import io from "socket.io-client";
socket = io('your backend host url')
socket.on('RECEIVE_TITLE', function(data)
Console. Log(data);
});
Frontend syntax could differ in angular.
As I am not familiar with angular
For more information visit.
Forclient side.
https://socket.io/docs/client-api/
For server.
https://socket.io/docs/server-api/
I am using the express framework and would like to connect to a mongodb without using mongoose, but with the native nodejs Mongodb driver. How can I do this without creating a new connection every time?
To handle get or post requests I currently open a new connection to the db for every request and close it on completion of the request. Is there a better way to do this? Thanks in advance.
Following the example from my comment, modifying it so that the app handles errors rather than failing to start the server.
var express = require('express');
var mongodb = require('mongodb');
var app = express();
var MongoClient = require('mongodb').MongoClient;
var dbURL = "mongodb://localhost:27017/integration_test";
var db;
// Initialize connection once
MongoClient.connect(dbURL, function(err, database) {
if(err) return console.error(err);
db = database;
// the Mongo driver recommends starting the server here
// because most apps *should* fail to start if they have no DB.
// If yours is the exception, move the server startup elsewhere.
});
// Reuse database object in request handlers
app.get("/", function(req, res, next) {
var collection = "replicaset_mongo_client_collection";
db.collection(collection).find({}, function(err, docs) {
if(err) return next(err);
docs.each(function(err, doc) {
if(doc) {
console.log(doc);
}
else {
res.end();
}
});
});
});
app.use(function(err, req, res){
// handle error here. For example, logging and
// returning a friendly error page
});
// Starting the app here will work, but some users
// will get errors if the db connection process is slow.
app.listen(3000);
console.log("Listening on port 3000");
var mongodb = require('mongodb');
var uri = 'mongodb://localhost:27017/dbname';
module.exports = function(callback) {
mongodb.MongoClient.connect(uri, callback);
};
Ad this snippet in a file say connect.js and then require this file(connect.js) in your file where you are declaring your functions for http requests.
On my local host, I have the following Node code to setup a mongoDB database name "dbname":
users.js:
var MongoClient = require("mongodb").MongoClient,
Connection = require("mongodb").Connection,
Server = require("mongodb").Server;
Users = function(host, port) {
var mongoClient = new MongoClient(new Server(host, port));
mongoClient.open(function (){});
this.db = mongoClient.db("dbname");
};
Users.prototype.getCollection = function (callback) {
this.db.collection("users", function (error, users) {
if (error) callback(error);
else callback(null, users);
});
};
Users.prototype.findAll = function (callback) {
this.getCollection(function (error, users) {
if (error) {
callback(error);
} else {
users.find().toArray(function (error, results) {
if (error) {
callback(error);
} else {
callback(null,results);
}
});
}
});
}
// Bunch of other prototype functions...
exports.Users = Users;
I like to put the above database functionality in one file, and then in my main server file require that file as follows:
server.js:
var Users = require("./users").Users;
var users = new Users("localhost", 27017);
users.findAll(function (err, user) {
// Do something
});
To have this working on localhost is pretty easy. In the command line, I just type the following:
$ mongod # to launch the database server
$ node server.js # to launch the web server
and it works fine. However, now I'm trying to push the whole thing onto Heroku with the mongolab addon
heroku addons:add mongolab
but the database is not running and I have no idea how to make it run. This tutorial explains how to setup mongodb with the mongolab URI, but that's not how my code works, I use a host and a port and I create a new server based on that. How should I change my code for it to work on the heroku app? I want to keep the database code in a separate file, with the prototype functions.
Follow the example here at the "MongoClient.connect" section.
Essentially, you will need to change this part of the code:
Users = function(host, port) {
var mongoClient = new MongoClient(new Server(host, port));
mongoClient.open(function (){});
this.db = mongoClient.db("dbname");
};
To use mongoClient.connect() instead of new MongoClient:
Users = function(url) {
MongoClient.connect(url, function(err, db) {
// Find better way to set this since this callback is asynchronous.
this.db = db;
});
};
If you are using node, I recommend using a library such as mongoose npm install mongoose to handle mongodb interactions. Look at my answer here for how to structure your schemas.
Helped by Xinzz's answer, here's the modified code, so that the mongodb database is initialized with a URI instead of host + port. That's how Heroku initializes the mongodb database, and that's why it wasn't working.
var mongodb = require("mongodb");
var MONGODB_URI = process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || "mongodb://localhost", // Make sure to replace that URI with the one provided by MongoLab
db,
users;
mongodb.MongoClient.connect(MONGODB_URI, function (err, database) {
if (err) throw err;
db = database;
users = db.collection("users");
accounts = db.collection("accounts");
var server = app.listen(process.env.PORT || 3000);
console.log("Express server started on port %s", server.address().port);
});
The key here is to declare the variables db and users upfront, assign them a value in the asynchronous callback of the connect function of MongoClient and also start the app (app.listen(...)) in the same callback. Then later in the code I can do the following:
users.find().toArray(function (err, results) {
// Do something
});
I also gave up on all these prototype functions, since they did not really add much.