I'm in the process of learning Node, Mongoose and Express by trying to build a CRUD API. When trying to "join" two MongoDB collections with the .populate function, I get an error that
db.collection(...).findOne(...).populate is not a function
My javascript chops are not great, so I've tried rewriting this in different ways to no avail.
server.js
require('./models/User')
require('./models/Product')
var db
mongodb.MongoClient.connect('mongodb://<username>:</password>#xxxx.mlab.com:xxx/xxx', (err, database) => {
if (err) return console.log(err)
db = database
app.listen(3000, function() {
console.log("listening on port 3000");
})
})
app.get('/api/users/:id', (req, res) => {
db.collection(USERS_COLLECTION).findOne({ id: (req.params.id) }).populate('product'), (err, doc) => {
if (err) handleError(res, err.message, 'Failed to get user')
res.status(200).json(doc)
console.log(req.params)
}
})
models/user.js
var mongoose = require('mongoose')
var UserSchema = new mongoose.Schema({
id: String,
first_name: String,
last_name: String
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}
})
mongoose.model('User', UserSchema)
Collections don't have the mongoose's model API including .populate. Instead of using collections directly, you should use the model that you registered in mongoose.
var schema = new mongoose.Schema(...);
var User = mongoose.model('User', schema);
// you can also get the model after it was registered via mongoose.model('User')
app.get('/api/users/:id', (req, res) => {
User.findOne({ id: (req.params.id) }).populate('product'), (err, doc) => {
if (err) handleError(res, err.message, 'Failed to get user')
res.status(200).json(doc)
console.log(req.params)
}
})
Related
This is my database connection:
app.js
const express = require("express");
const app = express();
var { MongoClient } = require("mongodb");
MongoClient.connect("mongodb://localhost:27017", (err, client) => {
if (err) return console.log(err);
db = client.db("MyDb");
app.listen(5000, () => {
console.log("listening on 5000");
});
});
And this is my insert function:
router.post(
"/register",
[
check("email")
.notEmpty()
.withMessage("Email Field is empty"),
check("email")
.isEmail()
.withMessage("Your email is not valid")
],
function(req, res) {
const errors = validationResult(req);
if (errors.length >= 0) {
res.render("register", { errors: errors.errors });
console.log(errors.errors);
return;
}
const { name, email, password } = req.body;
const newUser = new User({
name: name,
email: email,
password: password
});
newUser.save(function(err) {
if (err) throw err;
console.log(true);
});
}
);
And this is my user model:
User.js
const mongoose = require("mongoose");
const UserSchema = mongoose.Schema({
name: { type: String, require: true },
email: { type: String, require: true, unique: true },
password: { type: String, require: true },
created_at: Date,
updated_at: Date
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
There is no error in terminal or browser. When I click the "register" button, the app will freeze and there is no error message at all.
I already tested many tips concerning the database connection but couldn't solve the issue.
I find there are two order of problems in the proposed code, at least as we can read it in your question:
First, I can't find any binding between mongoose and the established mongodb connection
Second, your route handler does not seem to return any status code / content to the
caller
So, for as I see it, you can
change connection setup as follows
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true})
.then((conn, err) => {
app.listen(5000, () => {
console.log("listening on 5000");
});
});
in order to bind mongoose with MongoDb configuration
retust a status code, e.g. 201, when the new User has been saved:
newUser.save(function(err) {
console.log('Result', err)
if (err) throw err;
console.log(true);
res.send(201)
});
This way I prevent the application hanging up on receiving request...
I hope this can help you!
validationResult() "Extracts the validation errors from a request and makes them available in a Result object." https://express-validator.github.io/docs/validation-result-api.html Therfore, if you don't have any errors this object will contain no errors ( you can check with .isEmpty()), your endpoint doesn't send a response, and leaves the requestor waiting.
Im trying to make an API with node JS (express and jwt) with a mongodb.
I've make some routes (like to get markers for example), that's working fine.
BUT i've also make a route in order to post some markers. When I query it (that takes long time, and with Postman), and I've an error like :
"Could not get any response"
and in my console :
Error: socket hang up
My route controller :
const model = require('../models/markers');
module.exports = {
create: function(req, res, next) {
model.create({
param_1: req.body.param_1,
param_2: req.body.param_2,
param_3: req.body.param_3,
}, function (err, result) {
if (err)
next(err);
else
res.json({status: "success", message: "Marker added successfully", data: null});
});
},
getAll: function(req, res, next) {
let list = [];
model.find({}, function(err, items){
if (err){
next(err);
} else{
for (let item of items) {
list.push({
id: item._id,
param_1: item.param_1,
param_2 : item.param_2,
param_3: item.param_3
});
}
res.json({status:"success", message: "Markers list found", data:{markers: list}});
}
});
},
};
And for my route :
const express = require('express');
const router = express.Router();
const markerController = require('../controllers/markers');
router.get('/', markerController.getAll);
router.post('/', markerController.create);
module.exports = router;
My model :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const MarkerSchema = new Schema({
param_1: {
type: String,
required: true
},
param_2: {
type: String,
},
param_3: {
type: [{
type: String,
enum: ['0.5', '1', ',1.25', '1.5', '2', '3+']
}],
default: ['pending']
}
});
module.exports = mongoose.model('Marker', MarkerSchema);
My get router works fine but my post route don't.
You should check #Will Alexander comment, if it's a copy paste you have a typo in:
param_2: req.bodyparam_2,
at the moment I'm following a tutorial to push myself in the topics node + react.
The link to the repo is https://bitbucket.org/grtn/cloudstruct/src/develop/
When I make a post request to /api/users/register i get the following error in the console and i can't figure out why.
/Users/****/Dev/cloudstruct/routes/api/users.js:38
if(err) throw err;
^
Error: Illegal arguments: undefined, string
at _async (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:214:46)
at Object.bcrypt.hash (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:220:13)
at bcrypt.genSalt (/Users/****/Dev/cloudstruct/routes/api/users.js:37:28)
at Immediate.<anonymous> (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:153:21)
at runCallback (timers.js:756:18)
at tryOnImmediate (timers.js:717:5)
at processImmediate [as _immediateCallback] (timers.js:697:5)
[nodemon] app crashed - waiting for file changes before starting...
The Usermodel looks like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true
},
avatar:{
type: String
},
date:{
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('users', UserSchema);
And my routing:
const express = require ('express');
const router = express.Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
// Load User model
const User = require ('../../models/User')
//#route GET api/users/test
//desc Tests post route
//#access Public
router.get('/test', (req, res) => res.json({msg: '<h1>Hello World</h1>'}));
//#route POST api/users/register
//desc Register User
//#access Public
router.post('/register', (req, res) =>{
User.findOne({ email: req.body.email })
.then(user => {
if(user) {
return res.status(400).json({email: 'Email already exists'});
} else {
const avatar = gravatar.url(req.body.email, {
s: '200',
r: 'pg',
d: 'mm'
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar: avatar,
password: req.body.password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => res.json(user))
.catch(err => console.log(err));
})
});
}
});
});
module.exports = router;
Thanks for your help!
Your newUser.password is undefined. I am afraid that we can access mongoose docs like this. The solution in this case is to use req.body.password in .hash()
For reference: If you want to access the key value of a mongoose doc, your have to parse the doc into JSON.
The correct way to hash passwords with Mogoose is to use presave middleware. It will give you a guarantee that user password will always be hashed regardless of the place where you want to create a user record. Also, it's better for architecture: hashing will be made in the data layer instead of routing.
Here is a good example. Middleware is described in the step 2.
I am trying to build a Node API which uses MongoDB to create and fetch information from a file which contains Book information in the following JSON format:
{
Name: String,
Author: String,
Price: Number
}
I am unable to add info to the DB. I get the Book printed successfully message though. But when I see the DB, a JSON document is created only with an ID and the version key _v.
Below is my API code:
var express = require('express');
var app = express();
var bodyParser=require('body-parser');
var mongoose=require('mongoose');
var book = require('./automobile/reading/book')
//configuring the app for the bosy data intake for post operations
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
var PORT= process.env.PORT || 3000;
mongoose.connect('mongodb://localhost:27017/helloworldapi');
var router = express.Router();
app.use('/api', router);
router.use(function(req,res,next){
console.log('There is something happening in the API');
next();
})
router.route('/books')
.post(function(req,res){
var bookDB = new book();
bookDB.name=req.body.name;
bookDB.author=req.body.author;
bookDB.price=req.body.price;
bookDB.save(function(err){
if(err){
res.send(err);
}
res.json({message: 'Book was successfully printed'});
});
})
.get(function(req,res){
book.find(function(err, books){
if(err){
res.send(err);
}
res.json(books);
});
});
router.route('/books/:book_id')
.get(function(req,res){
book.findById(req.params.book_id, function(err, books){
if(err){
res.send(err);
}
res.json(books)
});
});
router.route('/books/name/:name')
.get(function(req,res){
book.find({name:req.params.name}, function(err, books){
if(err){
res.send(err);
}
res.json(books);
});
});
router.route('/books/author/:author')
.get(function(req,res){
book.find({author:req.params.author}, function(err, books){
if(err){
res.send(err);
}
res.json(books);
});
});
app.listen(PORT);
console.log('server listening on port '+PORT);
While trying to perform a GET Operation after performing the POST Operation, I am getting the below response:
[
{
"_id": "5a788cf1ad829e3aa4b91287",
"__v": 0
}
]
Below is my shema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BookSchema = new Schema({
Name: String,
Author: String,
Price: Number
});
module.exports= mongoose.model('book',BookSchema);
Please don't mind the root folder automobiles. Planned to create something else in the beginning.
You can use
router.route('/books')
.post(function(req,res){
var bookDB = new book(req.body);
bookDB.save(function(err){
if(err){
res.send(err);
}
res.json({message: 'Book was successfully printed'});
});
})
But the req.body values should be same on schema values.
I believe it is because you are not assigning to the right properties. In your route, you assign to the lowercase properties. However, you Schema defines uppercase properties. See if matching the case works:
router.route('/books')
.post(function(req,res){
var bookDB = new book();
bookDB.name=req.body.name; // This needs to match the property in the schema exactly
bookDB.author=req.body.author;
bookDB.price=req.body.price;
bookDB.save(function(err){
if(err){
res.send(err);
}
res.json({message: 'Book was successfully printed'});
});
})
var BookSchema = new Schema({
name: String, // Now I'm lower case
author: String,
price: Number
});
Or you can make your router uppercase:
router.route('/books')
.post(function(req,res){
var bookDB = new book();
bookDB.Name=req.body.name;
bookDB.Author=req.body.author;
bookDB.Price=req.body.price;
bookDB.save(function(err){
if(err){
res.send(err);
}
res.json({message: 'Book was successfully printed'});
});
})
I'm trying to create a many to many relationship with mongoose for when I am creating an employee. However I'm getting the following error when I call the .post:
TypeError: Employee.create(...).populate is not a function
My .get in which I also use .populate isn't throwing any errors. Which makes me wonder why .post does.
This is my code:
app.route('/api/employees')
.get(function (req, res, next) {
Employee.find()
.populate('statuses')
.exec(function (err, employee) {
if (err) {
return next(err);
}
res.json(employee);
});
})
.post(function (req, res, next) {
Employee.create(req.body)
Employee.findById(req.params._id)
.populate('statuses')
.exec(function (err, employee) {
if (err) {
return next(err);
}
res.json(employee);
});
});
This is the status class:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var statusSchema = new Schema({
name: ['In office', 'Project', 'Fired', 'Resigned', 'Ill']
});
module.exports = mongoose.model('Statuses', statusSchema);
And this is the employee class:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var employeeSchema = new Schema({
name: String,
division: ['IT','System','Trainee','Intern'],
statuses: [{type:Schema.Types.ObjectId, ref: 'Statuses'}],
project: Boolean,
comment: {type:String, default:null}
});
module.exports = mongoose.model('Employees', employeeSchema);
It seems the .post also seems to throw a 500 error, but I'm not sure if the two are related.
Is there an obvious error in above code or should I look for a mistake somewhere else?
The first thing you did wrong in the post request is that you never save the state, if the state saved then you save the status._id in the statuses.Then you do findById and find the employee._id and then you populate.
Here is an example:
Status.create(req.body, (err, status) => {
if (err) console.error(`Error ${err}`)
Employee.create({
name: req.body.name,
comment: req.body.comment,
division: req.body.division,
name: status._id
}, (err, employee) => {
if (err) console.error(`Error ${err}`)
Employee
.findById(employee._id)
.populate('statuses')
.exec((err, employee) => {
if (err) console.error(`Error ${err}`)
// console.log(JSON.stringify(employee))
res.json(employee);
})
})
})
In mongoose is not possible to populate after creating the object.
see Docs