Currently i am working on Express with Mongoose ODM to build a RESTful API for my mobile app. In my Mongoose Schema i have a title: Index field. I follow Mongoose Unique Index Validation Document to create Unique Document in MongoDB, bellow is my code
Mongoose Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CategorySchema = new Schema({
title: { type: String, required: true, index: true, unique: true },
details: { type: String, required: false },
thumbnail: { type: String, required: false },
created: { type: Date, default: Date.now },
modified: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Category', CategorySchema);
Express Code
var express = require('express');
var routes = express.Router();
var Category = require('./models/category.model');
routes.route('/categories')
.post(function(req, res) {
Category.on('index', function(error) {
if(error) {
res.send(error);
}
Category.create({ title: req.body.title, details: req.body.details,
thumbnail: req.body.thumbnail }, function(error) {
if(error) {
res.send(error);
}
res.json({ message: 'Category was save successfully..!' });
});
});
});
Problem:
Now my problem is that, when i send a POST request to my API http://localhost:3000/api/v1.0/categories. It will not send any response to my client application. This will not show any Warning or Error in server console.
I don't see the point in having
Category.on('index', function(error) {
if(error) {
res.send(error);
}
}
there. Do you want to listen to the index event inside a controller function? Probably not.
You can move
Category.create({ title: req.body.title, details: req.body.details,
thumbnail: req.body.thumbnail }, function(error) {
if(error) {
res.send(error);
}
res.json({ message: 'Category was save successfully..!' });
});
out of it. You should also make sure you return after sending a response, otherwise you might end up sending several responses. Inside if(error) you do a res.send(error) but it will continue to the res.json() later.
So if the rest of your code is correct then this might work:
var express = require('express');
var routes = express.Router();
var Category = require('./models/category.model');
routes.route('/categories').post(function(req, res) {
Category.create({
title: req.body.title,
details: req.body.details,
thumbnail: req.body.thumbnail
}, function(error) {
if(error) {
return res.status(500).json(error);
}
return res.status(201).json({ message: 'Category was save successfully..!' });
});
});
You will get an error message if you break the unique constraint for title. This will happen when you do the create. You have to restart the app before the unique constraint starts working.
Related
I have a simple ExpressJS/Node backend that contains a MongoDB database for which I use mongoose to interact. I can add objects to the db based on the UserSchema:
const userSchema = mongoose.Schema({
email : {
type: String,
required: true,
trim: true,
unique: 1
},
password : {
type: String,
required: true,
minlength: 5
},
name : {
type: String,
required: true,
maxlength: 30
},
lastname : {
type: String,
required: true,
maxlength: 30
},
cart : {
type : Array,
default: []
},
history : {
type: Array,
default: []
},
role : {
type: Number,
default : 0
},
token : {
type: String
}
});
From the express Server, I can register and add a new user to the DB and I know this works
Server.js
//========================================
// Register User
//========================================
app.post('/api/users/register', (req, res) => {
//create new User
const user = new User(req.body);
//save user
user.save((err, doc) => {
if(err)
return res.json({success: false, err});
res.status(200).json({
success : true,
userdata: doc
});
});
})
In User.js
//========================================
// SAVE in DB
//========================================
const User = mongoose.model('User', userSchema);
Now when I want to login, operation where I need to check the email and password match I encounter a problem when everything is fine and I want to add the JWT to the object all is good until it gets to the save method, there nothing happens and it doesn't respond anymore. It's like it goes in an infinite loop. I get error when something is wrong, but on the positive case, it disappears and sends no response, to either mongo, node, debug anything.
Server.js
app.post('/api/users/login', (req, res) => {
//find the email for the user
User.findOne({'email' : req.body.email} , (err, user) =>{
if(!user)
return res.json({loginSuccess : false, message : 'Authentication failed, email not found'});
//check the password
user.comparePassword(req.body.password, (error, isMatch) => {
if(!isMatch)
return res.json({loginSuccess : false, message : 'Wrong password'});
//generate token
user.generateToken((err, user) => {
if(err)
return res.status(400).send(err);
//store token as a cookie
res.cookie('w_auth', user.token).status(200).json({
loginSuccess : true
})
})
})
})
})
User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const SALT_I = 10;
require('dotenv').config();
//========================================
// User Login
//========================================
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(error, isMatch){
if(error)
return cb(error);
cb(null, isMatch);
})
}
userSchema.methods.generateToken = function (cb) {
var user = this;
var token = jwt.sign(user._id.toHexString(),process.env.SECRET)
user.token = token;
user.markModified('anything');
user.save(function(err,user){
if(err) return cb(err);
cb(null,user);
})
}
I get no more feedback in node console, debug, Mongo or even Postmen(I can wait here for minutes ) after user.save(...). I know it gets the good user and everything but I don't really know where to get from here. Also in Mongo I see no field for the token, I initially add an object with no token, can this affect everything? Is there another procedure to update an existing object in the collection?
In case GitHub is needed to see the code: Link
Indeed it's really strange, couldn't really debug what's wrong with this 'save' method. As a workaround, however, this one seems to work fine:
userSchema.methods.generateToken = function (cb) {
var user = this;
var token = jwt.sign(user._id.toHexString(), "mystupidsecret");
console.log("in generateToken");
console.log(user);
user.token = token;
console.log(user.token);
var email = user.email;
//save token
User.updateOne({ _id: user._id }, { $set: { token: token } }, function(err, user){
if(err) {
console.log(err);
return cb(err);
}
cb(null, user);
// this one is for debug only!
User.findOne({'email' : email} , (err, user) =>{
console.log("After update: ", user)
});
});
console.log('done');
}
It yields the following:
After update: { cart: [],
history: [],
role: 0,
_id: 5f3e48f09c7edc3f1c24a860,
email: 'abc233#wp.pl',
password:
'$2b$10$iDeeehLOzbQi3dawqW8Lg.HPOvcRBDIS/YD9D1EmqBOH9Be31WpX2',
name: 'ABCDEFGH',
lastname: 'Doeasdasdas',
__v: 0,
token:
'eyJhbGciOiJIUzI1NiJ9.NWYzZTQ4ZjA5YzdlZGMzZjFjMjRhODYw.aH9tCMbIK9t3CReiQg3Azln9Ca8xS7W0xL3qCMOKniY' }
I just start learning NodeJS and MongoDB. Now ı am working on a small project to learn basic stuff. But ı am getting a error, in this project ı try to insert data to mongodb which is,
userid like;
"5ed6bfe86034a81dbc7226aa" created from random numbers and letters.
But ı can not insert id to mongodb ı got an error. Here is my codes.
Firstly here is my model which is "Book.js"
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BookSchema = new Schema({
title: {
type: String,
required: true,
},
userid: Schema.Types.ObjectId, //ı defined here no problem.
published: {
type: Boolean,
default: false
},
So, in routes folder my "book.js" like this;
var express = require('express');
var router = express.Router();
//Models
const Book = require('../models/Book');
const User = require('../models/Users');
/* GET book page. */
router.post('/new', function(req, res, next) {
const book = new Book({
title: 'Node JS',
userid: BigInt, //ı got error here.
//what ı should write?
meta: {
votes: 12,
favs: 90
},
category: "History"
});
book.save((err, data) => {
if (err)
console.log(err);
res.json(data);
});
});
so when ı run my server and try to post data to mongodb with using postman ı got an error.
Like this; _message: 'book validation failed',
and more information: Error: book validation failed: userid: Cast to ObjectId failed for value "[Function: BigInt]" at path "userid"
Now, how can ı define "userid" variable? why ı got an error? what ı should write to define userid for mongodb to post data successfully? Thanks in advance.
I think you need to store the _id created in user schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BookSchema = new Schema({
title: {
type: String,
required: true,
},
userid: {
type:Schema.Types.ObjectId,
ref:User,
required:true
},
published: {
type: Boolean,
default: false
},
Then you need to fetch the user id and then store it to Book collection. By specifying ref keyword you can use populate method provided by mongoose to fetch the user info using book schema.
Example program to fetch and save _id
exports.postaddFoods=(req,res,next)=>{
const foodname=req.body.foodname;
const image=req.body.image;
const shopname=req.shop.shopName;
const price=req.body.price;
Shop.findOne({shopName:shopname})
.then(sid=>{
const food= new Food({
foodName:foodname,
image:image,
shopName:shopname,
price:price,
shopId:sid._id
});
return food.save()
})
This is not a solution
#Dharani Shankar , here is my routes/book.js
router.post('/new', (req,res,next) => {
Book.findOne({title: "NodeJS"})
.then(sid => {
const book = new Book({
title: "Node JS",
meta: {
votes: 12,
favs: 90
},
category: "History",
userid: sid._id
});
book.save((err, data) => {
if (err)
console.log(err);
res.json(data);
});
});
});
sorry for late reply. Note: I have to post my data from that url "localhost:3000/books/new" ı am using postman for testing.
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,
My Model Schema
const UserSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
tweets: []
});
This are the methods i use to communicate with mongo
module.exports.getUserByUsername = function(username, callback){
const query = {username: username}
User.findOne(query, callback);
}
module.exports.addTweet = function(newTweet, newUser, callback){
User.updateOne(newUser, {$push: newTweet}, (err, isUpdate) => {
if(err) throw err;
callback(null, isUpdate)
});
}
Im using NodeJS to code my backend, i already register a user and a login but when i try to post a tweet with that user i get an error realted with the _id and i never use the ids.
router.post('/post', passport.authenticate('jwt', {session:false}), (req, res, next) => {
let newTweet = new User({
tweets:{
title: req.body.title,
body: req.body.body
}
})
User.getUserByUsername(req.body.username, (err, usert) => {
if(err) throw err;
if(!usert){
return res.json({success: false, msg: 'User not found'});
}
User.addTweet(newTweet, usert, (err, isUpdate) =>{
if(err) throw err;
if(isUpdate){
return res.json({success: true, msg: "Tweet Post"});
}
});
});
});
The Error
This is the error i get using PostMan
/home/daniel/react/miapp/Back/node_modules/mongodb/lib/utils.js:132
throw err;
^
MongoError: The field '_id' must be an array but is of type objectId in document {_id: ObjectId('5b26b4e911c67c4cfa6917e4')}
at Function.MongoError.create (/home/daniel/react/miapp/Back/node_modules/mongodb-core/lib/error.js:45:10)
at toError (/home/daniel/react/miapp/Back/node_modules/mongodb/lib/utils.js:149:22)
at /home/daniel/react/miapp/Back/node_modules/mongodb/lib/collection.js:1035:39
at /home/daniel/react/miapp/Back/node_modules/mongodb-core/lib/connection/pool.js:541:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Your getUserByUsername() returns a document fromthe mongo collection like
{_id: Object("...."), .....}
If you just want the username add a project query to your getUserByUsername() as:
const project = {_id:0,username:1}
User.findOne(query, project,callback)
This returns only the username of the document.
Also change the definition of new tweet to:
let newTweet = {tweets: {title: req.body.title,body: req.body.body}}
Edit: What you can also do is let your getUserByUsername code as before instead change your updateOne code(define newTweet as mentioned above):
User.updateOne({_id: newUser._id}, newTweet, callback)
Ideally, you should project only the _id from the mongo collection and query it while updating as it not only saves you from the network throughout of retreiving unnecessary data but the update query is also fast due to indexing.