Unable to fetch any items from MongoDB database - node.js

My connection with the mongodb server gets established but I am unable to fetch any items from there.
This is a simple query to get all items from the User collection.
User.find({}, function (err, result) {
if (err) {
console.log(err);
} else {
res.json(result);
}
});
My User schema is very simple as well:
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
minlength: 5,
maxlength: 35,
},
password: {
type: String,
required: true,
minlength: 5,
maxlength: 100,
},
username: {
type: String,
required: true,
minlength: 5,
maxlength: 20,
},
});
module.exports = {
User: mongoose.model("user", userSchema),
};
What can I do?

Is the User.find() call inside of a route? If not, res.json() will not work. Try console.log ing it.

Spelling mistake on my end. Fixed!

Related

How get user name from the userId to display in a post?

Hi everyone I am trying to add the user's name in the post that is being created by that user but I'm running into trouble.
This is the part of the post where the user's name should be displayed
<Link style={{ textDecoration: "none", color: "black" }}>
<h4
onClick={() => this.handleShowUserProfile(event.userId)}
className="host-name"
>
{getUser(event.userId).name}
</h4>
</Link>
This is where the user is being grabbed from the database
import http from "./httpService";
const apiEndPoint = "http://localhost:3100/api/users";
export function getUsers() {
return http.get(apiEndPoint);
}
export function getUser(userId) {
return http.get(apiEndPoint + "/" + userId);
}
in the backend this is how the user schema looks like
const jwt = require("jsonwebtoken");
const config = require("config");
const Joi = require("joi");
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
bio: {
type: String,
required: true,
minlength: 200,
maxlength: 400
},
interests: {
type: Array
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
password: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
isAdmin: Boolean
});
userSchema.methods.generateAuthToken = function() {
const token = jwt.sign(
{ _id: this._id, isAdmin: this.isAdmin },
config.get("jwtPrivateKey")
);
return token;
};
const User = mongoose.model("User", userSchema);
function validateUser(user) {
const schema = {
name: Joi.string()
.min(5)
.max(50)
.required(),
bio: Joi.string()
.required()
.min(200)
.max(400),
interests: Joi.array().required(),
email: Joi.string()
.min(5)
.max(255)
.required()
.email(),
password: Joi.string()
.min(5)
.max(255)
.required()
};
return Joi.validate(user, schema);
}
module.exports.User = User;
module.exports.validate = validateUser;
this is the event schema...
const Joi = require("joi");
const mongoose = require("mongoose");
const { categorySchema } = require("./category");
const { userSchema } = require("./user");
const Event = mongoose.model(
"Events",
new mongoose.Schema({
image: {
type: String
},
title: {
type: String,
required: true,
minlength: 5,
maxlength: 50,
trim: true
},
user: {
type: userSchema,
required: true
},
details: {
type: String,
required: true,
minlength: 200,
maxlength: 300,
trim: true
},
category: {
type: categorySchema,
required: true
},
numOfAttendies: {
type: Number,
required: true,
min: 3,
max: 10000
}
})
);
this is the handleShowUserProfile
handleShowUserProfile = id => {
this.setState({
showUserProfile: true,
shownUserID: id,
user: getUser(id)
});
};
handleHideUserProfile = () => {
this.setState({
showUserProfile: false
});
};
If I had to guess, getUser() likely returns a promise, not a value. You'll need to look into reading the name from props(state) and updating state instead of trying to do it all right there.
Try logging out that get request. See if the results it returns match up with what you're trying to get from it.
export function getUser(userId) {
const results = http.get(apiEndPoint + "/" + userId);
console.log("results of getUser ::: ", results)
return results
}
The getUser function is an asynchronous function so it will return a promise in most cases you need to use state and the lifecycle methods to fetch and set the username.
Can you update your handleShowUserProfile method like the following?
handleShowUserProfile = async (id) => {
const user = await getUser(id);
this.setState({
showUserProfile: true,
shownUserID: id,
user: user
});
};
I assume your api returns data for http://localhost:3100/api/users/:userId endpoint.
After you check your api and sure that it returns data, you need to check if you send a valid userId in this line.
onClick={() => this.handleShowUserProfile(event.userId)}
Lastly you should also change the following line since the user data is in the state.
{getUser(event.userId).name} => {this.state.user && this.state.user.name }

minLength property in User model not working

Everything is working perfectly fine except for the minLength property of password.
If I send { "email" : "harshit#example.com", "password": "abc" } from Postman,
it still works even though I've set minLength to 6.
minLength property of email is working perfectly good but not of password.
server.js
app.post('/users', (req, res) => {
var body = _.pick(req.body, ['email', 'password']);
var user = new User(body);
user.save().then((doc) => {
res.send(doc)
}).catch( (err) => {
res.send(err)
})
});
user.js // using mongoose here.
var User = mongoose.model('User', {
email: {
type: String,
required: true,
trim: true,
minLength: 5,
unique: true,
validate: {
validator: validator.isEmail ,
message: `{VALUE} is not a valid E-Mail`
}
},
password: {
type: String,
required: true,
minLength: 6, // This line isn't working
trim: true
},
tokens: [{
access: {
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
According to the Mongoose documentation you're supposed to use minlength, notice the lowercase l.

Populate array of ObjectId's in response using node js

I am new to programming in NodeJS and was looking for solutions around how to populate response attributes in json Object which are of type Object Array.
Below is the sample response from my service -
{
"Roles": [
"5b7fd72537651320a03494f8",
"5b7fdc06e3fcb037d016e26a"
],
"_id": "5b8e530be2d4630fd4f4b34a",
"Username": "ctucker",
"FirstName": "Chris",
"LastName": "Tucker",
"Email": "ctucker#innovate.com",
"createdAt": "2018-09-04T09:40:27.091Z",
"updatedAt": "2018-09-04T09:40:27.091Z",
"__v": 0
}
I would like to populate the ObjectId's in the Roles array attribute with the actual name instead of the Ids.
How should I be doing this.
Following is the way, I have defined the User and Roles schema
User Model
const userSchema = new mongoose.Schema({
Username: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
FirstName: {
type: String
},
LastName: {
type: String
},
Email: {
type: String,
required: true,
minlength: 5,
maxlength: 255
},
Password: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
Roles: [{type: mongoose.Schema.Types.ObjectId, ref: Roles}],
Active: {type: Boolean, default: true},
SuperUser: {type: Boolean, default: false}
},{
timestamps: true
});
Roles Model
const rolesSchema = new mongoose.Schema({
RoleName: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
Description: {
type: String
},
Active: {type: Boolean, default: true}
}, {
timestamps: true
});
Create User
router.post('/', [auth, admin], async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ Username: req.body.Username });
if (user) return res.status(400).send('User with username: ', req.body.Username, 'already exists. Please try with any other username');
let roleInput = [];
if(req.body.Roles !== null) {
req.body.Roles.forEach( async (element) => {
objectId = mongoose.Types.ObjectId(element);
roleInput.push(objectId);
});
}
console.log('Role info ', roleInput);
user = new User(_.pick(req.body, ['Username', 'FirstName', 'LastName' ,'Email', 'Password', 'Active','SuperUser']));
console.log('User Input => ', user);
const salt = await bcrypt.genSalt(10);
user.Password = await bcrypt.hash(user.Password, salt);
roleInput.forEach( async (objectId) => {
user.Roles.push(objectId);
});
console.log('User Input after role addition => ', user);
await user.save();
const token = user.generateAuthToken();
res.header('x-auth-token', token).send(_.pick(user, ['_id', 'FirstName', 'LastName' ,'Email', 'Roles']));
});
I would like to get the RoleName instead of the ObjectId in response.
Any help in this regard would be appreciated.
Forgot to mention that I already tried using the populate method, but I see the following exception.
How I am using the populate method -
router.get('/', auth, (req, res, next) => {
var request_params = url.parse(req.url,true).query;
var searchQuery = (request_params.mode === 'active') ? {'Active': true} : {};
User.find(searchQuery)
.populate('Roles')
.select(['-Password', '-Active', '-SuperUser'])
.then((users) => {
res.send(users);
}, (error) => {
next(error);
});
});
Exception -
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<h1>Schema hasn't been registered for model "[object Object]".
Use mongoose.model(name, schema)</h1>
<h2></h2>
<pre>MissingSchemaError: Schema hasn't been registered for model "[object Object]".
Use mongoose.model(name, schema)
at new MissingSchemaError (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\error\missingSchema.js:20:11)
at NativeConnection.Connection.model (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\connection.js:791:11)
at getModelsMapForPopulate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:4016:20)
at populate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3505:21)
at _populate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3475:5)
at utils.promiseOrCallback.cb (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3448:5)
at Object.promiseOrCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\utils.js:232:14)
at Function.Model.populate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3447:16)
at cb (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\query.js:1678:17)
at result (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:414:17)
at executeCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:406:9)
at handleCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:128:55)
at cursor.close (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\operations\cursor_ops.js:211:62)
at handleCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:128:55)
at completeClose (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\cursor.js:887:14)
at Cursor.close (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\cursor.js:906:10)</pre>
</body>
</html>
Understood the problem.
Within the User schema definition, I had to define the Roles Schema as well.
const rolesSchema = new mongoose.Schema({
RoleName: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
Description: {
type: String
},
Active: {type: Boolean, default: true}
}, {
timestamps: true
});
const Roles = mongoose.model('Roles', rolesSchema);
const userSchema = new mongoose.Schema({
Username: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
FirstName: {
type: String
},
LastName: {
type: String
},
Email: {
type: String,
required: true,
minlength: 5,
maxlength: 255
},
Password: {
type: String,
required: true,
minlength: 5,
maxlength: 1024
},
Roles: [{type: mongoose.Schema.Types.ObjectId, ref: 'Roles'}],
Active: {type: Boolean, default: true},
SuperUser: {type: Boolean, default: false}
},{
timestamps: true
});
After adding the roles schema definition within the user schema, the response from postman does populate the RoleName in response.
{
"Roles": [
{
"RoleName": "AC-User"
},
{
"RoleName": "AC-PMLead"
}
],
"_id": "5b8e48e2f3372a098c6e5346",
"Username": "mcmillan",
"FirstName": "McMillan",
"LastName": "McMillan",
"Email": "mcmillan#innovate.com",
"createdAt": "2018-09-04T08:57:07.029Z",
"updatedAt": "2018-09-04T08:57:07.029Z",
"__v": 0
}
Simple using mongoose populate() function
const user = await User.findOne().populate('Roles');
// output => user.Roles // will be array of mongo documents
Cheers
You can use Mongoose's populate to populate RoleName instead of ObjectId refs
Mongoose has a more powerful alternative called populate(), which lets you reference documents in other collections.
'use strict';
let user = await User.findOne({
Username: req.body.Username
}).populate('Roles');
if (user && user.Roles.length > 0) {
for (let role of user.Roles) {
console.log(role.RoleName);// logs `RoleName`
}
}

Having an issue getting user id

So I am working on building a web app and I have a lot of it working. However I am trying to link my user._id to my items database. But when I try to post an item using postman the app crashes saying it cant read property '_id' of null. I know I am missing something but I honestly can't figure out what other code I need to implement. Any help would be great. Thanks
Here is the code for the UserSchema:
const mongoose = require('mongoose');
const passportLocalMongoose = require("passport-local-mongoose");
const UserSchema = new mongoose.Schema({
username: {
type: String,
trim: true,
unique: true,
required: true,
minlength: 3,
maxlength: 15
},
firstName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
lastName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
email: {
type: String,
unique: true,
required: true
},
resetPasswordToken: String,
resetPasswordExpires: Date,
isAdmin: {
type: Boolean,
default: false
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("user", UserSchema);
Here is the code for the ItemSchema:
const mongoose = require('mongoose');
const User = require('./user');
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 20
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
createdBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model("items", ItemSchema);
And here is the code for the route thats throwing the error:
const express = require("express");
const router = express.Router();
const User = require("../models/user");
router.route("/item/add")
.post(function(req, res) {
User.findById(req.user._id, function(user, err) {
if (err) {
console.log(err);
}
var item = new Item();
item.name = req.body.name;
item.description = req.body.description;
item.price = req.body.price;
item.createdBy = { id: req.user._id, username: req.user.username };
item.save(function(err) {
if (err) {
res.send(err);
}
res.json({ message: "Item was successfully saved" });
console.log(item);
});
});
});
You need to send your data in json format in postman for example:
{'id':1, 'name':'jhon doe', 'email':'jhondoe#example.com'}
in your backend file you need to call
req.body.id not req.user._id

Mongoose - How do you update a specific object inside an array of reference objects?

I have this schema:
var UserSchema = new Schema({
name: String,
username: {
type: String,
required: [true, "Please enter a username"],
minlength: [6, "Username must be at least 6 characters"],
maxlength: [15, "Username cannot exceed 15 characters"],
unique: true
},
password: {
type: String,
required: [true, "Please enter a password"],
minlength: [6, "Password must be at least 6 characters"],
maxlength: [17, "Password cannot exceed 17 characters"],
},
roommates: [{roomate: {type: Schema.Types.ObjectId, ref: "User"}, status: {type: String, default: "pending"}}]
})
This is my controller (the id's are just two random Id's of users I have in my db, so ignore that:
update: function(req, res) {
User.findOne({
_id: "57f1645c05ec06099ead3db6", 'roommates._id': "57f16436190a09099a1ddbde"
}, {'roommates.$': 1}, function(err, user1) {
user1.update()
})
}
}
My controller is totally useless right now. The objective is to reach inside that roommates array, find ONE specific object, and UPDATE just that one so it's status changes from 'Pending' (which is the default) to a new string value that I can set in the controller.
I'm getting lost in the queries. How would you do this using mongoose?
Thanks
Here is the answer:
update: function(req, res) {
User.findOneAndUpdate({
_id: "57f16436190a09099a1ddbde", "roommates._id": "57f1645c05ec06099ead3db6"
}, {$set: {"roommates.$.status": "active"}}, function(err, res) {
if (err) {
console.log(err);
} else {
console.log("status updated");
}
})
}

Resources