I have a User model through Mongoose, and I'm trying to create a simple CRUD API, starting with the Users part, problem is that my "index," and "create" routes seem to be working but the detail page won't seem to accept the variable being passed to it, and I can't render things on the page.
My User schema is very simple for now, just to get the API working (./models/user.js)
const userSchema = new Schema({
name: {
type: String,
maxlength: 50,
minlength: 2,
trim: true
}
})
The controllers for the routes that DO work look like this (./controllers/user_controller.js)
exports.index = function(req, res, next) {
User.find({}, (err, users) => {
res.render('userindex', {users: users})
})
}
exports.new = function(req, res, next) {
newUser = new User({ name: req.body.name })
newUser.save((err, user) => {
if (err) res.send(err)
res.redirect('/users')
})
}
exports.addUser = function(req, res, next) {
res.render('newuser')
}
The routes look like this so far (./routes/userRouter.js)
I do recognize that the routes for add and new are reversed, it'll be fixed.
userRouter.get('/', userController.index)
userRouter.get('/new', userController.addUser)
userRouter.post('/add', userController.new)
userRouter.get('/:id', userController.detail)
The only thing left is the broken controller: (./controllers/user_controller.js)
exports.detail = function(req, res, next) {
User.find({'_id': req.params.id}, (err, user) => {
if (err) res.send(err)
res.render('userdetail', {user: user})
})
}
And the template that won't seem to receive the data. (./views/userdetail.pug)
p #{user.name}
Find returns an array, so there's no user to template. Either template the first item on the collection:
res.render('userdetail', {user: user[0]})
or use .findOne instead.
Related
I'm trying to learn mongoose with nodejs and created a following node function as below where in I'm receiving the email ID from the Angular application and based on the EmailId trying to find the records in MongoDb:
const express = require('express');
const mongoose = require('mongoose');
var bodyParser= require("body-parser");
const port = 3000;
const app = express();
//setting up bodyParser middleWare
app.use(bodyParser.urlencoded({"extended":true}))
app.use(bodyParser.json());
//setting mongoose connection to local mongoDb
mongoose.connect("mongodb://127.0.0.1/userDetails",{useNewUrlParser:true,useUnifiedTopology:true})
const db = mongoose.connection;
var userSchema = new mongoose.Schema({
email:{type:String},
userId:{ type: String, required: true },
password:{ type: String, required: true }
});
var users = mongoose.model('User', userSchema);
//error handler middleWare
app.use(function(err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
});
app.post("/login",async(req,res,next)=>{
try {
await users.find({ email:"xyz#gmail.com"}).exec(function (err, docs) {
if (err){
console.log(err);
}
else{
console.log("First function call : ", docs);
}
})
}
catch(err){
next(err);
}
})
app.listen(port,()=>{console.log("server started")});
The above code shows no error, but the output is always First function call : [].My mongodb collection is as follows
db.User.insertMany([
{ email: "xyz#gmail.com", userId:1,password : "123#124" },
{ email: "abc#yahoo.com",userId:2,password : "123#125"},
{ email: "lmn#outlook.com", userId:3,password : "123#126"}
])
db.User.find({ email: "xyz#gmail.com"});
Where is that I'm going wrong ,what should be the approach to get the data?
Your code should find and print the data to the console assuming the email address you're looking for is actually in the collection. However, I want to point out a few things that don't quite make sense.
There is absolutely no reason to await the function call await users.find({ email:"xyz#gmail.com"}).... So you should change it to users.find({ email:"xyz#gmail.com"})
With that change, there is absolutely no reason for the router function to be async. So this is how it should look app.post("/login", (req, res, next) => {...
You're not sending a response back to the caller of /login. So when you test, it just hangs there until you cancel your request. You should add res.json({msg: 'ok'}) below console.log("First function call : ", docs);
And finally, mongoose is an awesome, promise-based library. Why not use its promise capability? It will greatly clean up your code. Here is how I would re-write your code:
app.post("/login", (req, res, next) => {
users.find({email:"xyz#gmail.com"})
.then(docs => {
console.log(docs);
res.json(docs);
})
.catch(e => {
console.log(e);
next(e);
})
})
If I am using this function I am getting proper response.
router.get('/', function(req, res, next) {
var products = Product.find();
res.render('shop/index', { title: 'Shopping-Cart', products: products });
});
If I am using below function my local server is not giving any response and keep loading.
router.get('/', function(req, res, next) {
Product.find(function (err, docs){
res.render('shop/index', { title: 'Shopping-Cart', products: docs });
});
});
Help me to solve this problem.
Try this. Add match object to find method.
router.get('/', function(req, res, next) {
Product.find({}, function (err, docs){
res.render('shop/index', { title: 'Shopping-Cart', products: docs });
});
});
The second one is simply wrong. This whole thing :
function (err, docs){
res.render('shop/index', { title: 'Shopping-Cart', products: docs });
}
is being passed to the find() method as a parameter.It makes absolutely no sense
Correct would be this:
Product.find().then(docs=>{
res.render('shop/index', { title: 'Shopping-Cart', products: docs })
}).catch(err=>console.log(err))
Since you haven't specified the database you are using, I'll assume something similar to MongoDB. The first parameter to the find operation must be a query object if any.
Documentation
So, you can tweak your code in any of the ways like this:
var products = await Product.find(); // Using async-await
res.render('shop/index', { title: 'Shopping-Cart', products: products });
Product.find({}, function(err, products) { // Using callbacks
res.render('shop/index', { title: 'Shopping-Card', products: products });
}
Product.find().then(function(products) { // Using Promises
res.render('shop/index', { title: 'Shopping-Card', products: products });
}
I'm building a REST API in nodejs/express/mongodb/mongoose. I've started using MongoDB/Mongoose recently and I still have some doubts.
What I'm trying to achieve is to access a specific user bag (a user can have multiple bags) and also I want to be able to add to that bags participants/payers. (a user bag can have multiple participants/payers)
My mongoose user modal contains the rest of the schemas. I created a schema for each one because I believe it would be easier to find a given bag or participant directly because of the ObjectId (not sure if this is correct).
Mongoose Modal/Schemas:
const PayerSchema = new Schema({
name: {
type: String
},
amount: {
type: Number
}
});
const BagSchema = new Schema({
name: {
type: String
},
type: {
type: String
},
payers: [PayerSchema]
});
const UserSchema = new Schema({
name: {
type: String,
required: [true, 'User name field is required']
},
bags: [BagSchema]
});
module.exports = mongoose.model('User', UserSchema);
I was able to create the CRUD controller methods for a new user, but I still not sure on:
Creating a new bag for a specific user (I was able to do this but not sure if it's the right way)
Creating a new participant in a specific bag for a specific user. (addPayer method is wrong need help here)
Check out my controller user/bags/participants methods:
const User = require('../models/userModel');
getAllUserBags: (req, res, next) => {
User.findById({ _id: req.params.id }).then((user) => {
res.send(user.bags);
})
.catch(next);
},
getOneUserBag: (req, res, next) => {
console.log(req.params.bagid);
User.find({ 'bags._id': req.params.bagid}, {"bags.$" : 1}).then((obj) => {
res.send(obj);
})
.catch(next);
},
createBag: (req, res, next) => {
let bag = req.body.bag;
User.findOneAndUpdate(
{_id: req.body.id},
{$push: {bags: bag}
}).then(() => {
//Unnecessary - just to return update user with new bag.
User.findOne({_id: req.body.id}).then((user) => {
res.send(user);
})
}).catch(next);
},
addPayer: (req, res, next) => {
let payer = req.body.payer;
User.find(
{'bags._id': req.params.bagid},
{"bags.$" : 1},
{$push: {payers: payer}
}).then((obj) => {
console.log(obj);
//Unnecessary - just to return update user with new bag.
// User.findOne({_id: req.body.id}).then((user) => {
// res.send(user);
// })
}).catch(next);
}
Thanks for the help
Base on what we discuss, your User schema is good enough for your requirements, as long as making sure that one User document does not exceed the 16MB limit of MongoDB document.
Creating a new bag for a specific user (I was able to do this but not sure if it's the right way)
Yours is fine. However, there are some improvements:
createBag: (req, res, next) => {
User.findByIdAndUpdate(req.body.id, {
$push: { bags: req.body.bag }
}, {
new: true // this will make the query getting the updated document
})
.then(user => {
res.json(user);
})
.catch(next);
})
Creating a new participant in a specific bag for a specific user. (addPayer method is wrong need help here)
Since you decided to nest the 'bags', the bag.id might be duplicated among User documents. See this to understand the possibility. Thus, I recommend using an userId along with bagId:
getOneUserBag: (req, res, next) => {
User.findOne({
_id: req.params.userId,
bags._id: req.params.bagId
})
.then(user => {
if (!user) res.status(404).end();
let bag = user.bags.id(req.params.bagId);
res.json(bag);
})
.catch(next);
}
addPayer: (req, res, next) => {
User.findOneAndUpdate({
_id: req.params.userId,
bags: $elemMatch: {
_id: req.params.bagId
}
}, {
$push: { 'bags.$.payers': req.body.payer } // Use 'positional $' operator along with $elemMatch in the query to update only the matched bag
}, {
new: true // Do not forget the 'new' options to get the updated document
})
.then(user => {
if (!user) res.status(404).end();
res.json(user);
})
.catch(next);
}
and in the router
router.get('/users/:userId/bags/:bagId', getOneUserBag);
router.post('/users/:userId/bags/:bagId/payers', addPayer);
In the getAllUserBags(), you use the wrong syntax for User.findById():
getAllUserBags: (req, res, next) => {
User.findById(req.params.id) // Not { _id: req.params.id }
.then((user) => {
res.json(user.bags);
})
.catch(next);
}
I am building a mean stack app.
When I use this :
router.get('/courses/:course', function (req, res) {
res.json(req.course);
});
I get back the full object. But when I do this :
router.get('/courses/:course/reviews', function (req, res){
res.json(req.course.reviews);
});
I only get back the reviews ID, not the full elements like I whished it did.
Any idea why ?
EDIT :
Answering questions :
here is router.param :
router.param('course', function (req, res, next, id) {
var query = Course.findById(id);
query.exec(function (err, course){
if(err) { return next(err);}
if(!course) {return next(new Error('can\'t find course'));}
req.course = course;
return next();
});
});
Course is a model :
var CourseSchema = new mongoose.Schema({
code: String,
name: String,
courseContentGrade: Number,
courseTeachingGrade: Number,
courseAverage: Number,
reviews: [{ type: mongoose.Schema.Types.ObjectId, ref: 'review'}]
});
You need to populate the review field using mongoose.populate().
Below is a revised router.param, note the addition of the populate method to the query.
router.param('course', function (req, res, next, id) {
var query = Course.findById(id).populate('reviews');
query.exec(function (err, course){
if(err) { return next(err);}
if(!course) {return next(new Error('can\'t find course'));}
req.course = course;
return next();
});
});
Documentation: http://mongoosejs.com/docs/populate.html
This seems like a relatively simple issue, but I can't seem to find good documentation. I'd like to pass json data from mongodb into a route, so that it is available in my ejs template.
My schema is:
var GiveSchema = new Schema({
title: String,
shortname: String,
contents: String,
image: String,
category: String
});
module.exports = mongoose.model('GiveData', GiveSchema);
var Givedata = mongoose.model( 'GiveData' );
I'd like to pass make it available to my route below, as the variable list:
app.get('/', function(req, res) {
res.render('index.ejs',{
list: Givedata,
bootstrappedUser: req.user,
page: 'home'
});
});
You'll still need to query the database for your items.
app.get('/', function(req, res, next) {
Givedata.find(function(err, items){
if(err) { return next(err); }
res.render('index.ejs',{
list: items,
bootstrappedUser: req.user,
page: 'home'
});
});
});