I was familiar with MongodB for CRUD operation. Here, I'm trying to make simple post request on mongodB atlas but I want to know where I have done error for the connection and posting data to MongodB atlas.
Model.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let quizSchema = new Schema({
title: {
type: String,
},
description: {
type: Number,
},
question: {
type: String,
},
});
const Quiz = mongoose.model("Quiz", quizSchema);
module.exports = Quiz;
index.js
I'm trying to create the database collection name "QuizDatabase" and insert the data to it.
var express = require("express");
var bodyParser = require("body-parser");
const Quiz = require("./views/model/model");
var Request = require("request");
var app = express();
app.use(bodyParser.json());
const MongoClient = require("mongodb").MongoClient;
const uri =
"mongodb+srv://username:password#cluster0.iom1t.mongodb.net/QuizDatabase?retryWrites=true&w=majority";
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
mongoose.connect(uri);
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
app.post("/new/", function (req, res) {
Quiz.collection("QuizDatabase").insertMany(req.body, function (err, doc) {
if (err) {
handleError(res, err.message, "Failed to create new quiz.");
} else {
res.status(201).send(JSON.stringify(body));
}
});
});
function handleError(res, reason, message, code) {
console.log("ERROR: " + reason);
res.status(code || 500).json({ error: message });
}
You dont have to use mongo client if you are already using mongoose.
In index.js file just import the model
const Quiz = require("./model");
And you are already using mongoose to connect to db when you write mongoose.connect(uri); You don't have to use client.connect() again.
Query to insert -
Quiz.insertMany(req.body);
Your index file should look like this -
const Quiz = require("./views/model/model");
var Request = require("request");
var app = express();
app.use(bodyParser.json());
const uri =
"mongodb+srv://username:password#cluster0.iom1t.mongodb.net/QuizDatabase?retryWrites=true&w=majority";
mongoose.connect(uri);
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
app.post("/new/", function (req, res) {
Quiz.insertMany(req.body, function (err, doc) {
if (err) {
handleError(res, err.message, "Failed to create new quiz.");
} else {
res.status(201).send(JSON.stringify(body));
}
});
});
function handleError(res, reason, message, code) {
console.log("ERROR: " + reason);
res.status(code || 500).json({ error: message });
}
There are several reasons.
Connection Issues to the MongoDB database.
To check this insert app.listen() into mongoose connect. This would make sure you can only run development on your preferred PORT only when it has successfully connected to your Database. e.g From your code
mongoose.connect(uri)
.then(() => {
//listen for PORT request
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
}).catch((error) => {
console.log(error);
});
Try purposely using the wrong Username or Password and see if you get this error:
MongoServerError: bad auth : Authentication failed.
at Connection.onMessage (/Users/user/Documents/..<pathway>../connection.js:207:30)
*
*
*
*
ok: 0,
code: 8000,
codeName: 'AtlasError',
[Symbol(errorLabels)]: Set(1) { 'HandshakeError' } }
If you don't get this error then you have a connection problem. To solve this, I added my current IP ADDRESS and 0.0.0.0/0 (includes your current IP address) at the Network Access page. So you click on MY CURRENT IP ADDRESS and confirm upon setting up the network. Go to NETWORK ACCESS, click on add new IP ADDRESS, input 0.0.0.0/0 and confirm. Then try using the wrong username or password in the URI link given to you to see if it gives the above-expected error, then you can now correct the Username and Password, and npm run dev or npm start (However you configured it in your package.json file).
Code issues
First of I would correct your Model.js file from this:
const Quiz = mongoose.model("Quiz", quizSchema);
module.exports = Quiz;
to this:
module.exports = mongoose.model("Quiz", quizSchema);
I can see why yours can work, but it may be an issue as you want to get the schema upon accessing the whole file.
Secondly, I would correct the code for Posting and you can do that in 2 ways using the asynchronous method. Which depends on the method of assigning the req.body.
Way 1:
app.post("/new/", async (req, res) => {
const { title, description, question } = req.body;
//adds doc to db
try {
const quiz = await Quiz.create({ title, description, question });
res.status(200).json(quiz);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
OR
Way2:
app.post("/new/", async (req, res) => {
const quiz = new Quiz(req.body);
//adds doc to db
try {
const savePost = await quiz.save();
response.status(200).send(savePost);
} catch (error) {
response.status(400).send(error);
}
});
NOTE: You don't necessarily have to create a named database and collection in Mongo Atlas before starting the project. The URI given to you covers that if there are no problems with the connection to the DB or the Code.
based on your code
URI:
"mongodb+srv://username:password#cluster0.iom1t.mongodb.net/QuizDatabase?retryWrites=true&w=majority";
would create a database called: QuizDatabase and collection called: quizs (MongoDb always creates the plural word from the model given and makes it start with lowercase (i.e from your Model.js, the mongoose.model("Quiz"))).
If no database is named in your URI, then a database called test is automatically created for you as a default database, with the collection name being the mongoose.model("") given.
CONCLUSION
This should solve at least 90% of your issues, any other creation/POST problems is currently beyond my current expertise. Happy Coding 🚀🚀🚀
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'm trying to get data in JSON format. I just copied an old project and changed it IP address to database, username, port, password and database name.
When I try to access data through this addres: localhost:3000/&id=13
The browser just doesn't load them.
When I enter the address with the port without / I see the message with error:
return res.status(500).json({ error: "Грешна заявка. Опитай отново !"})
The same code is pinned to another database and I see the data in JSON format.
I checked 10 times if the username, password, port and database name are correct and they are fine.
The code:
// Create express app
var express = require("express")
var app = express()
var mysql = require('mysql')
var express = require("express")
var cors = require('cors')
app.use(cors())
// Server port
var HTTP_PORT = 3000
var pool = mysql.createPool({
connectionLimit: 10,
host: '192.168.0.1',
user: 'user',
port: '3388',
password: 'password',
database: 'databasename'
});
var ardaforecast = '';
app.route('/')
.get(function (req, res) {
// omitted
res.setHeader('Access-Control-Allow-Origin', '*', 'Cache-Control', 'private, no-cache, no-store, must-revalidate');
//const date = req.query.date;
const id = req.query.id;
pool.query(`CALL Get_Alert_levels_Station(${id})`, function (error, result) {
if (error)
return res.status(500).json({ error: "Грешна заявка. Опитай отново !"})
aladinModel = result;
res.json({ ardaforecast })
});
});
// Start server
app.listen(HTTP_PORT, () => {
console.log("Server running on port %PORT%".replace("%PORT%", HTTP_PORT))
});
pool.on('error', function (err) {
console.log(err.code); // 'ER_BAD_DB_ERROR'
});
app.use(function (req, res) {
res.status(404);
})
;
Can I get an example of how I can fix this or how to find out where the problem is ?
You can use this one to see how what your url contains: https://www.freeformatter.com/url-parser-query-string-splitter.html
In your example, the problem is that you're using & (ampersand), but what it does is separating multiple query parameters. Since you have just one, your url is not properly structured and does not contain any parameters whatsoever.
You should use ? (question mark) to denote the first one:
localhost:3000/?id=13
p.s. Успех ;)
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 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
I'm using Express.js and MongoLab and I followed the Heroku setup to get MongoDB working in production throwing this code in my app.js.
//Mongo on Heroku Setup
var mongo = require('mongodb');
var mongoUri = process.env.MONGOLAB_URI ||
process.env.MONGOHQ_URL ||
'mongodb://localhost/mydb';
mongo.Db.connect(mongoUri, function (err, db) {
db.collection('mydocs', function(er, collection) {
collection.insert({'mykey': 'myvalue'}, {safe: true}, function(er,rs) {
});
});
});
and I have the following routes and field for my email form (also in app.js):
//Routes
app.get('/', function(req, res) {
res.render('index', {
title: 'DumbyApp'
});
});
//save new email
app.post('/', function(req, res){
emailProvider.save({
address: req.param('address')
}, function( error, docs) {
res.redirect('/')
});
});
This renders the new form on the index page and lets me save it locally but not in production because I don't know how to setup my email collection. Can anyone walk me through this? brand new to using MongoDB and Node.js, so could use some help.
EDIT:
In The MongoLab Database Interface, I made a collection called emails. Is this the right course of action?
EDIT 2:
Here's defining EmailProvider in app.js along with the file itself.
app.js
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, EmailProvider = require('./emailprovider').EmailProvider;
var emailProvider= new EmailProvider('localhost', 27017);
emailprovider.js
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;
EmailProvider = function(host, port) {
this.db= new Db('localdb', new Server(host, port, {safe: false}, {auto_reconnect: true}, {}));
this.db.open(function(){});
};
EmailProvider.prototype.getCollection= function(callback) {
this.db.collection('emails', function(error, email_collection) {
if( error ) callback(error);
else callback(null, email_collection);
});
};
//save new email
EmailProvider.prototype.save = function(emails, callback) {
this.getCollection(function(error, email_collection) {
if( error ) callback(error)
else {
if( typeof(emails.address)=="undefined")
emails = [emails];
for( var i =0;i< emails.address;i++ ) {
email = emails[i];
email.created_at = new Date();
}
email_collection.insert(emails, function() {
callback(null, emails);
});
}
});
};
exports.EmailProvider = EmailProvider;
While the connection code in the first code box appears to be correct, the emailProvider object isn't using it. Instead, in app.js, the EmailProvider is being connected to localhost:27017 and the database name is hardcoded in emailprovider.js as 'localdb'.
What you want to do instead is use the connection information provided in the MONGOLAB_URI environment variable in your EmailProvider, which contains the host, port, and database name already.
There are a number of ways to go about doing this, but one way would be to move your connection code from that first code box into the EmailProvider constructor, and then change the constructor so that it takes a URI instead of a host and port. That way, you can pass the MONGOLAB_URI variable to the constructor in app.js.