I am writing a very simple application with NodeJS and Mongoose.
If I disable authentication in Mongoose, everything works fine and I can access my records from the database. But when I turn on the authentication and configure my NodeJS code to use authenticated Mongoose connection it doesn't let me query my records and the web page keeps on loading.
Name of my database is "bears".
P.S. I have created my users in "Admin database and Bears" database. I have given a user of books database readwrite permissions and it works fine when I authenticate it through "Mongo" command or db.auth command. But it is not working through NodeJS/Mongoose.
Here is my code.
var mongoose = require('mongoose');
var opt = {
user: 'bearsdev',
pass: 'bearsdev123!',
auth: {
authdb: 'bears'
}
};
mongoose.connect('mongodb://localhost:27017/bearsdev',opt);
var Schema = mongoose.Schema;
var BearSchema = new Schema({
name: String
});
module.exports = mongoose.model('Bear', BearSchema);
In my server.js
var express = require('express'); // call express
var app = express(); // define our app using express
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var Bear = require('../models/bear');
var bear = require('express').Router();
bear.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
bear.get('/bear',function(req, res) {
Bear.find(function(err, bears) {
if (err)
res.send(err);
res.json(bears);
});
});
app.use('/test', bear);
app.listen(8080);
localhost:8080/test works.
localhost:8080/test/bear keeps on loading.
I have tried different ways of authentication with Mongoose, e.g.
mongoose.connect('mongodb://bearsdev:bearsdev123!#localhost:27017?authSource=bearsdev');
and
mongoose.connect('mongodb://bearsdev:bearsdev123!#localhost:27017/bearsdev');
None of these ways are working for me.
I hope following code may be work.
database = {
host: 'localhost',
db: 'bears',
port: '27017',
options: {
user: "bearsdev",
pass: "bearsdev123!",
auth: {
authdb: 'admin'
}
}
}
mongoose.connect(database.host, database.db, database.port, database.options, function (err) {
if (err) {
console.log("connection error:", err);
} else {
console.log("MongoDB connection successful");
}
});
I was able to solve the problem by uninstalling local node module of Mongoose and then installing the latest version. Probably some intermediate version (~3.6.13) had that bug. But latest Mongoose is good to go.
Related
edit: added image of collection
edit2: after a few debugging, i tried to save a document and run the find() query, and it worked (i found the test document i saved). i think the problem now is somewhere in the connection to the db. i may be connecting to somewhere else
edit3 changed title because it doesnt seem to point to the actual problem.
after a few debugging, i found out that mongoose is querying to somewhere else aside from the database i specified in uri. when i tried to save a test document, it was saved successfully and i was able to get it back using find(). However when i used find(), it only returned the previously saved test document. So now im wondering where are these documents saved?
to add more details, here are some of the codes ive written:
apiServerRoutes.js
'use strict';
module.exports = function(app) {
var apiServer = require('../controllers/apiServerControllers');
app.route('/items')
.get(apiServer.get_all_item);
app.route('/items/:itemId')
.get(apiServer.get_item);
};
apiServerControllers.js
'use strict';
var mongoose = require('mongoose');
var Item = mongoose.model('item_m');
exports.get_all_item = function(req, res) {
console.log("get_all_item() is called");
Item.find({}, function(err, item) {
if (err)
res.send(err);
res.json(item);
});
};
exports.get_item = function(req, res) {
console.log("get_item() is called");
Item.findOne({item_typeId: req.params.typeId}, '',function(err, item) {
if (err)
res.send(err);
res.json(item);
});
};
ItemSchema.js
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ItemSchema = mongoose.Schema({
item_name: String,
item_desc: String,
item_type: String,
});
var Item = mongoose.model('item_m', ItemSchema, 'item_m');
module.exports = Item;
server.js
var express = require('express'),
app = express(),
port = process.env.PORT || 3000,
mongoose = require('mongoose'),
Item = require('./api/models/ItemSchema'),
bodyParser = require('body-parser');
mongoose.Promise = global.Promise;
mongoose.set('debug', true);
mongoose.connect('mongodb+srv://[[user]]:[[pass]]#market-test-app-1dza9.mongodb.net/test?retryWrites=true&w=majority', {
useNewUrlParser: true, useUnifiedTopology: true});
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log("Connection Successful!");
});
var routes = require('./api/routes/apiServerRoutes');
routes(app);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.listen(port);
console.log('RESTful API server started on: ' + port);
end of edit3
the stuff below this are from the original question i had before i did some digging and debugging
So I have this controller function which uses a simple find query with no projection parameters
exports.get_all_item = function(req, res) {
Item.find({}, function(err, item) {
if (err)
res.send(err);
res.json(item);
});
};
Everytime I run my request in postman, I get an empty result. When I turned debug mode on in express, I saw this query sent by mongoose
Mongoose: item_m.find({}, { projection: {} })
When I try to run this query in mongodb, i get this error
"message" : ">1 field in obj: {}",
If I do add a parameter for projection eg. item_name, mongoose sends this:
Mongoose: item_m.find({}, { projection: { item_name: 1 } })
and when run that query in mongodb, i get this error
"message" : "Unsupported projection option: projection: { item_name: 1.0 }",
But when I change the query to
db.item_m.find({}, { item_name: 1 } )
It works fine, returning the results that Im expecting.
Im still new to both express, node.js, and mongodb. Is this some version problem between mongodb and mongoose?
Im using 4.0.13 for mongodb and 5.8.3 for mongoose but from what I researched, this should be fine. What other stuff am I missing here? Or what ever stuff should I check into that I may have missed?
Thanks in advance.
I use fetch all query using this method. I have tweaked it for your need.
exports.get_all_item = (req, res, next) => {
Item.find()
.select("item_name")
.exec()
.then(items => {
if (items.length > 0) {
res.status(200).json(items);
} else {
res.status(204).json({
message: "No items available"
});
}
})
.catch(error => {
next(error);
});
};
Hope it helps :)
When I checked back in my DB using mongodb atlas, I found that there were two existing databases: master (where I keep my data) and test (im not sure how this was created, but it existed). The entire time mongoose was accessing test and not master. I changed the 'test' in my URI to 'master' and it worked fine.
So I guess the lesson here is to double check the URI; and when debugging, try saving a sample data and find where that data is saved.
I am trying to insert a sub document into all existing documents in a collection in db,in nodejs using express framework.Following is the code snippet:
updatedoc: function(update,options,cb)
{
return this.update({},update,options).exec(cb);
}
where parameters update and options are as follows :
const update = { $push: { "defaultads": content }};
const options = { multi: true};
It seems to run and gives the following output on the console :
{ n: 1, nmodified: 1, ok: 1 }
but no push takes place at all ,in any of the documents of the database.
I have checked :
1) whether i am pushing in the right db.
2) whether correct values are being passed
However I am not able to find where I am going wrong.
I am new to nodejs and would really appreciate guidance in solving this problem.
Thanks in advance.
I am giving a simple code with full fledged requirement of yours. First create a config.js using this file you will be connected to mongodb.Here is the code
module.exports = {
'secretKey': '12345-67890-09876-54321',
'mongoUrl' : 'mongodb://localhost:27017/product'
}
Next create a models folder . Keep this schema in this models folder . I named it as product.js. Here is the code
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var imageSchema = new Schema({
imagepath:{
type:String
}
});
var nameSchema = new mongoose.Schema({
productName:{type: String},
productPrice:{type: Number},
imagePaths:[imageSchema]
});
module.exports = mongoose.model("product", nameSchema);
Next create a routes folder and keep this routes code in this folder I named it as route.js. Here is the code
var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var Product = require('../models/product');
var app = express();
var Router = express.Router();
Router.use(bodyParser.json());
Router.get('/product',function(req,res){
Product.find({}, function (err, product) {
if (err) throw err;
res.json(product);
});
})
Router.post('/productData',function(req, res, next){
Product.create(req.body, function (err, product) {
if (err) throw err;
console.log('Product Data created!');
var id = product._id;
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Added the product data with id: ' + id);
});
})
Router.post('/subdocument',function (req, res, next) {
Product.find({},function (err, result) {
if (err) throw err;
for(var i=0;i<result.length;i++){
result[i].imagePaths.push(req.body);
result[i].save(function (err, ans) {
if (err) throw err;
console.log('SubDocument created!');
});
}
res.send("Successfully added");
});
})
module.exports = Router;
Next server code I named it as app.js. Here is the code
var express = require('express');
var bodyParser = require('body-parser');
var Product = require('./models/product');
var mongoose = require('mongoose');
var config = require('./config');
mongoose.connect(config.mongoUrl);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
console.log("Connected correctly to server");
});
var app = express();
var route=require('./routes/route');
app.use('/route',route);
app.listen(3000,function(){
console.log("Server listening on 3000");
});
Run the server as node app.js.
API's
Use GET method http://localhost:3000/route/product .This is for getting all the product information.
Use POST method http://localhost:3000/route/productData .This for creating document. Post data in json format through request body like
{
"productName" : "sweets",
"productPrice" : "33"
}
You will see the response like this.
First post some documents i.e., post 2 or 3 documents and then you can see the documents with get API as I mentioned above. Then you will see all the documents contains empty sub document. You will see the response like this
And now add the sub document to all using the below api.
Use POST method http://localhost:3000/route/subdocument . Using this you can add a sub document to all the documents like this you need to add sub document
{
"imagepath" : "desktop"
}
You can see the response like this
After this again when you run the get API you can see all the sub documents are added to all documents.
Hope this helps.
I have a MongoDb server hosted on Azure. I'm now building a Node.js API meant to retrieve data from a table on one of the databases (i.e. table: Word; database: MyDatabase). I've built the API following this tutorial, but I'm unable to successfully retrieve any data from it...
I know the server is up and running and also reachable since I can tcp-connect to it through:
psping [Azure's Public IP]:27017
Now, I have an node.js api with the following code:
1) app/server.js
var express = require('express'); // call express
var app = express(); // define our app using express
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
mongoose.connect('mongodb://[Azure's public IP]:27017/MyDatabase');
var Word = require('./models/word');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080; // set our port
// ROUTES FOR API
var router = express.Router(); // get an instance of the express Router
// middleware to use for all requests
router.use(function(req, res, next) {
// do logging
console.log('Something is happening.');
next();
});
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
router.route('/words')
.get(function(req, res) {
Word.find(function(err, words) {
if (err)
res.send(err);
res.json(words);
});
});
// more routes for our API will happen here
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);
// START THE SERVER
// =============================================================================
app.listen(port);
console.log('Magic happens on port ' + port);
I've also written a model for my only table within the database, which has 3 columns: the auto-generated ObjectId, Spanish, French (meant to have words in both languages to make it work as a translator). The models looks like this: 2) app/models/word.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var WordSchema = new Schema({
spanish: String,
french: String
})
var Word = mongoose.model('Word',WordSchema);
module.exports = Word;
Now, I go to postman and GET on the following: http://localhost:8080/api/words; which returns [].
On MongoDb logs I see the following:
2016-08-05T03:16:26.520+0000 I NETWORK [conn60] end connection [Some IP]:[Some port] (1 connections now open)
2016-08-05T03:31:11.878+0000 I NETWORK [initandlisten] connection accepted from [Some IP]:[Some port] #61 (1 connection now open)
As you mentioned in your comment that the documents were retrieved from db.word.find() I think I found the problem. You need to put documents into collection named words, instead of word.
Mongoose will use the plural version of your model name. See http://mongoosejs.com/docs/models.html for more information.
I think you are missing {} when doing find.
router.route('/words')
.get(function(req, res) {
Word.find({}, //Added here.
function(err, words) {
if (err)
res.send(err);
console.log(words)
res.json(words);
});
});
Hope this will help.
EDIT:-
According the document of doc, the find function accept the first parameter as an object and treat it as conditions, but not a callback function.
Nothing important
My first question here on stackoverflow. I've used it for years to find answers, but now I need a bit of guidance. I'm new to node and express and the async way of structuring an app.
Goal - A REST interface with validation and neDB database
I got the following code working. POST a new user is the only route. It's based on many answers and tuts mixed together. I find it hard to scaffold out the logic, to get a structure you can build on.
I'm not sure at all whether this structure is crap or not. Any advice would be appreciated.
Main file is initializing the database, middleware validator, and starting the app.
// rest.js
var express = require('express'),
bodyParser = require('body-parser'),
validator = require('express-validator'),
db = require('./database/db'),
userRouter = require('./routers/users');
db.init();
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(validator());
app.use('/api/users', userRouter);
var port = process.env.PORT || 8080;
app.listen(port);
Database
This question/answer made me create the small database module with alterations.
How do you pass objects around node express application?
It doesn't have much attention. Maybe because it's very obvious or maybe not a good answer.
The idea is that whem i get multiple collections, they all get initialized on startup, but I can request a single collection if that's all the module needs, or I can get the entire db object back if another module would require that.
// database/db.js
var nedb = require('nedb');
var db = {};
db.init = function() {
db.users = new nedb({ filename: './database/data/users', autoload: true });
db.users.ensureIndex({ fieldName: 'username', unique: true }, function (err) {});
db.users.ensureIndex({ fieldName: 'email', unique: true }, function (err) {});
};
db.get = function(collection) {
if (collection && db[collection])
return db[collection];
return db;
}
module.exports = db;
Router
I require the User Model here and use the express-validator and sanitizes the request before passing it on to the model, based on a minimalist key schema in the model. I don't have any controllers. If I had (or when I do), I would put the validation there. The router is supposed to send the response and status right?
// routers/users.js
var express = require('express'),
_ = require('lodash'),
User = require('../models/user');
var userRouter = express.Router();
userRouter.route('/')
.post(function(req, res) {
req.checkBody('username', 'Username must be 3-20 chars').len(3,20);
req.checkBody('email', 'Not valid email').isEmail();
req.checkBody('password', 'Password must be 6-20 chars').len(6,20);
var err = req.validationErrors();
if (err) {
res.status(422).send(err);
return;
}
var data = _.pick(req.body, _.keys(User.schema));
User.create(data, function (err, newData) {
if (err) {
res.status(409).send(err);
} else {
res.status(201).send(newData);
}
});
});
module.exports = userRouter;
Model
The model requires the database module and gets the "connection". Is this OK?
// models/user.js
var db = require('../database/db');
var User = function (data) {
this.data = data;
};
User.schema = {
_id: null,
username: null,
email: null,
password: null
};
User.create = function (data, callback) {
db.get('users').insert(data, callback);
};
module.exports = User;
Thanks for reading this far. Now, my question is:
Is there something fundamentally wrong with this setup, concerning the database usage and the validation logic. I know the model looks stupid :)
I am receiving an empty array with the following code:
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'mytestapp');
var SurveySchema = require('../models/Survey.js').SurveySchema;
var Survey = mongoose.model('SurveySchema', SurveySchema, 'surveys');
var UserSchema = require('../models/Survey.js').User;
var User = mongoose.model('user', UserSchema, 'users');
exports.getSurveysForUser = function(User) {
return function (req, res) {
User
.findOne({_id: req.params.userId})
.populate('surveys')
.exec(function (err, user){
if (err) return res.json({error: err})
else {
var surveyList=[];
surveyList = user.surveys;
console.log(surveyList);
console.log("user: "+ user);
res.json(surveyList);
}
});
}};
This is the console output:
[ ]
user: { __v: 2,
_id: 52939b8c22a7efb720000003,
email: 'a#b.de',
password: '202cb962ac59075b964b07152d234b70',
surveys: []
}
These are the Mongoose models:
exports.SurveySchema = new Mongoose.Schema({
description : String,
questions : [question] });
exports.User = new Mongoose.Schema({
name : String,
email: { type: String, unique: true },
password: { type: String, required: true},
surveys : [{type: Schema.ObjectId, ref: 'SurveySchema'}] });
Btw:
I already tried User.findOne(...) and then a Survey.find() in the callback. It seemed that the second statement was not even executed. Apparently i am very new to mongoose..and i can't find a way around this problem
Do you have any ideas how to help me?
I couldn't really find any helpful solution here, but the problem shouldn't be a big one.
Thanks in advance, its really keeping me up for days now!!
Edit: So this is the index.js with the method:
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'mytestapp');
var SurveySchema = require('../models/Survey.js').SurveySchema;
var Survey = mongoose.model('SurveySchema', SurveySchema, 'surveys');
var UserSchema = require('../models/Survey.js').User;
var User = mongoose.model('user', UserSchema, 'users');
//.. here are some more methods..
exports.getSurveysForUser = function(User) {
return function (req, res) {
User
.findOne({_id: req.params.userId})
.populate('surveys')
.exec(function (err, user){
if (err) return res.json({error: err})
else {
var surveyList=[];
surveyList = user.surveys;
console.log(surveyList);
console.log("user: "+ user);
res.json(surveyList);
}
});
}};
//this is the code, that saves a response to a survey
exports.addResponse = function(ResponseSet) {
return function (req, res) {
console.log("bin da: addResponse");
console.log("response zu: " + req.body.surveyId);
console.log("von user : " + req.body.userId);
//für user speichern
var pUser = User.findOne({_id:req.body.userId}, function (error, user) {
// Maybe populate doesnt work, because i only push the ID?
user.surveys.push(Mongoose.Types.ObjectId(req.body.surveyId));
user.save();
}
);
var pSurvey = Survey.findOne({_id:req.body.surveyId}, function (error, survey) {
survey.responses.push(Mongoose.Types.ObjectId(req.params.id));
survey.save();
}
);
//responseSet speichern
var responseSet = new ResponseSet(req.body);
responseSet.save(function(error, responseSet) {
if (error || !responseSet) {
res.json({ error : error });
} else {
res.json(responseSet);
}
});
};
};
And this is the app.js, which consumes the index.js:
var Mongoose = require('mongoose');
var db = Mongoose.createConnection('localhost', 'mytestapp');
var SurveySchema = require('./models/Survey.js').SurveySchema;
var Survey = db.model('surveys', SurveySchema);
var UserSchema = require('./models/Survey.js').User;
var User = db.model('user', UserSchema);
var ResponseSetSchema = require ('./models/Survey.js').responseSet;
var ResponseSet = db.model('responseSet', ResponseSetSchema);
var express = require('express')
, routes = require('./routes')
, http = require('http')
, path = require('path')
, passport = require('passport')
, pass = require('./config/pass')
, user_routes = require('./routes/user');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views/app');
//app.engine('html', require('ejs').renderFile);
app.use(express.static(__dirname + '/views/app'));
app.use(express.cookieParser());
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'securedsession' }));
app.use(passport.initialize()); // Add passport initialization
app.use(passport.session()); // Add passport initialization
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.all('/secure', pass.ensureAuthenticated);
app.get('/', function (req, res)
{
res.render('index.html');
} );
// some more code...
app.get('/api/secure/userSurveys/:userId', routes.getSurveysForUser(User));
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
Hope it helpsto fight the problem!
Many many thanks in advance!! :)
Try changing:
surveys: [{type: Schema.ObjectId, ref: 'SurveySchema'}] });
to
surveys: [{type: Schema.Types.ObjectId, ref: 'SurveySchema'}] });
Also, make sure you have surveys pushed as children to User.surveys.
http://mongoosejs.com/docs/populate.html, here this section talks about your requirement in detail:
Refs to children
We may find however, if we use the aaron object, we are unable to get a list of the stories. This is because no story objects were ever 'pushed' onto aaron.stories."
Ok, it took quite a lot to figure out the issue:
exports.getSurveysForUser = function(User) {
...
};
The var User - model is not being injected into the scope properly. Change to:
exports.getSurveysForUser = (function(User) {
...
})(User);
The returned function's signature for middleware do not require User model to be passed as they are re-initiated and passed within the middleware code.
In index.js, change this
app.get('/api/secure/userSurveys/:userId', routes.getSurveysForUser(User));
to
app.get('/api/secure/userSurveys/:userId', routes.getSurveysForUser());
I also request you to self test your code and read as many docs as possible. Also, there can be multiple ways of reaching your goals. With time and practice you will conquer them all. Good luck!
So i found a solution!
Firstly it seemed, that the mongoose Schemas were not correctly required.
So in the models, i did mongoose.model('modelname', schemaname); for every model and now i only use mongoose.model(...) for every model in the index.js.
Secondly i found out about an even more critical thing: There were suddenly no user.surveys for my testuser anymore! I am sure that it was filled with surveys a few days ago. Because i tested my code several times and some surveys were pushed to that collection. Maybe i dropped the collection it in some testing..i don't really remember. So i pushed a survey manually in the mongodb console and tested it again --> worked! the user.surveys were populated! maybe the function worked yesterday and didn't need any change. I am so sorry, if that was a waste of time.
Bad thing is, that right now the exports.addResponse(....) is only saving a response, but is not pushing the IDs to the arrays user.surveys and survey.responses. This seems to be a synchronizing Problem and i will figure that out somehow.
Anyways, thank you for your help and time!