i´m using mongoose in my node.js - express. In my schema model i´m using the package mongoose-unique-validator to check that user email is unique, if the email already exist i would get and error ValidationError: User validation failed: email: Error, expected "email" to be unique. Value: "example#example.com" (which is fine). i decide to turn my promises from mongoose to rxjs observables this way:
controller.ts
creatUser(req: Request, res: Response, next: NextFunction) {
from(bcrypt.hash(req.body.password, 10))
.pipe(
mergeMap((hash) => {
const avatartPath = this.utilities.generateImgPath(req);
const user = this.userAdatper.userAdatper(req.body, { password: hash, avatar: avatartPath });
return of(new User(user).save()).pipe(catchError((error) => of(error)));
})
)
.subscribe(
(user) => {
console.log() // Promise is resolve here on the validation error return an empty object
res.status(201).send(user);
},
(err) => {
console.log(err);
res.status(500);
const error = new Error(`Internal Server Error - ${req.originalUrl}`);
next(error);
}
);
}
**Schema**
const UserSchema = new Schema({
user_rol: {
type: String,
default: 'subscriber',
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
requiered: true,
},
fullName: {
type: String,
requiered: true,
},
email: {
type: String,
requiered: true,
unique: true,
},
password: {
type: String,
requiered: true,
},
avatar: {
type: String,
requiered: true,
},
favorites: [
{
type: [Schema.Types.ObjectId],
ref: 'ArcadeItems',
},
],
updatedOn: {
type: Date,
required: true,
},
created: {
type: Date,
default: Date.now,
},
});
UserSchema.plugin(uniqueValidator);
but for some reason if the promise fail, it resolve on the success callback after subscription returning and empty object and no the error callback, try to implement catchError() from rxjs operators but no success.
Replace of(new User(user)...) for from(new User(user)...) and on cathError() return throwError(error)
creatUser(req: Request, res: Response, next: NextFunction) {
from(bcrypt.hash(req.body.password, 10))
.pipe(
mergeMap((hash) => {
const avatartPath = this.utilities.generateImgPath(req);
const user = this.userAdatper.userAdatper(req.body, { password: hash, avatar: avatartPath });
return from(new User(user).save()).pipe(catchError((error) => throwError(error)));
})
)
.subscribe(
(user) => {
console.log() // Promise is resolve here on the validation error return an empty object
res.status(201).send(user);
},
(err) => {
console.log(err);
res.status(500);
const error = new Error(`Internal Server Error - ${req.originalUrl}`);
next(error);
}
);
}
Related
In my case, if I unselect any field in postman, I got an error in the terminal but I want to print that error message or custom error in the postman response. how to do that?
POST method
router.post("/admin/add_profile", upload.single("image"), async (req, res) => {
try {
const send = new SomeModel({
first_name: req.body.first_name,
last_name: req.body.last_name,
phone: req.body.phone,
email: req.body.email,
image: req.file.filename,
});
send.save();
const result = await s3Uploadv2(req.files);
res.json({ status: "Everything worked as expected", result });
} catch (err) {
res.status(404).send(err.message);
}
});
schema.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const SomeModelSchema = new Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: ["last name is required"],
},
phone: {
type: Number,
required: ["Phone number is required"],
unique: true,
validate: {
validator: (val) => {
return val.toString().length >= 10 && val.toString().length <= 12;
},
},
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: ["email address is required"],
validate: (email) => {
return /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/;
},
match: [
/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/,
"Please fill a valid email address",
],
},
image: {
data: Buffer,
contentType: String
}
});
module.exports = mongoose.model("SomeModel", SomeModelSchema);
here I unselected the first_name field but I got an error in the terminal I want to print that error or a custom error in the postman response.
error message in terminal
You should await the save() call, which returns a Promise.
You should then be able to handle the error in the catch block:
router.post('/admin/add_profile', upload.single('image'), async (req, res) => {
try {
const send = new SomeModel({
first_name: req.body.first_name,
last_name: req.body.last_name,
phone: req.body.phone,
email: req.body.email,
image: req.file.filename,
});
await send.save();
const result = await s3Uploadv2(req.files);
res.json({ status: 'Everything worked as expected', result });
} catch (error) {
if (error.name === "ValidationError") {
let errors = [];
for (const err in error.errors) {
errors.push(err.message)
}
return res.status(400).send(errors);
}
res.status(500).send('Server error');
}
});
I have a simple express application that insets comments into posts, the issue is that the comments are never inserted but no errors are shown when post via postman it properly returns the post but with no comments.
Just try: this and this but seems to not working
This is my schema
interface PostAttrs {
userid: mongoose.Schema.Types.ObjectId;
username: string;
date: Date;
text: string;
image: string;
comments: Array<any>;
likes?: number;
}
const postSchema = new Schema<PostAttrs>({
userid: {
type: mongoose.Schema.Types.ObjectId,
required: true,
},
username: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
},
text: {
type: String,
required: true,
},
image: {
type: String,
required: false,
},
comments: [
{
required: false,
date: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
text: {
type: String,
required: true,
},
},
],
likes: {
type: Number,
required: true,
},
});
And the API route
export const createComment = async (req: Request, res: Response) => {
try {
const postId = req.params.postId;
const userId = req.params.userId;
const comment = req.body.comment;
var commentObj = {
date: new Date(),
userId: userId,
text: comment
};
await Post.findOneAndUpdate(
{ _id: postId },
{ new: true },
{$push: {
comments: { commentObj }
}},
(err: any, doc: any) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
return res.status(200).send(doc);
}
);
} catch (error) { }
}
What's wrong with my code?
SOLVED: The problem was the order of the parameters in the findOneAndUpdate() sentence, first the search condition, next, the value to update, and finally the statement. So I had to change this
await Post.findOneAndUpdate(
{ _id: postId },
{ new: true },
{$push: {
comments: { commentObj }
}},
(err: any, doc: any) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
return res.status(200).send(doc);
});
to
await Post.findOneAndUpdate(
{ _id: postId },
{$push: {
comments: { commentObj }
}},
{ new: true },
(err: any, doc: any) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
return res.status(200).send(doc);
});
When using 'await' with Mongoose's methods like findOneAnd.... the method is not run unless you explicitly do so.
Try:
await Post.findOneAndUpdate(......).exec();
Also when using the await keyword you can refactor and remove the callbacks
When I try to access an response obj from mongoose, it doesn't not return anything. Following is my code
//app.ts
import { User } from './Models/User';
router.get('/reset/:token', async (req, res) => {
const user = await User.findOne({ _id: '60a12460479e874db836d939' }).lean()
console.log(user)//returns an object with email property
console.log(user.email) //returns undefined
return res.json({
user,
})
})
Whereas this works fine
//app.ts
import { User } from '../Models/User';
router.get('/reset/:token', async (req, res) => {
const user = await User.findOne({ _id: '60a12460479e874db836d939' }, (err: any, user: any) => {
if (err) { console.log(err); }
console.log(user.email)//returns email
})
return res.json({
user,
})
})
I have tried researching about this problem. Most of them seem to point out schema. But my schema seems to be fine.
Following is my schema
//Models/User.ts
import mongoose from 'mongoose'
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false
},
resetPasswordToken: String,
resetPasswordExpires: Date
})
export const User = mongoose.model('User', UserSchema);
Please advise.
Thank you.
This is my Profile Schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
// Special field type because
// it will be associated to different user
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
company: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
status: {
type: String,
required: true,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
},
githubusername: {
type: String,
},
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: {
type: String,
required: true,
},
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
This is my view api. It doesn't work. it only return Cast to ObjectId failed for value { 'experience._id': '5edcb6933c0bb75b3c90a263' } at path _id for model profile
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findById({
'experience._id': req.params.viewexp_id,
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
How can I fix this? I tried looking at the stackoverflow of the same errors. still it doesn't seem to work.
and this is what I am trying to hit
The problem is that you have to convert your string _id to mongoose object id using this function mongoose.Types.ObjectId and my suggestion is to use findOne function instead of findById,
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
let id = mongoose.Types.ObjectId(req.params.viewexp_id);
const exp = await Profile.findOne(
{ "experience._id": req.params.viewexp_id },
// This will show your sub record only and exclude parent _id
{ "experience.$": 1, "_id": 0 }
);
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findOne({
'experience._id': mongoose.Types.ObjectId(req.params.viewexp_id),
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
You are saving object id . but your param id is string. convert it in ObjectId. Please check my solution.
router.post(
"/",
[
auth,
[
check("status", "status is required").not().isEmpty(),
check("skills", "skills is required").not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
company,
website,
location,
bio,
status,
githubuername,
skills,
youtube,
facebook,
twitter,
instagram,
linkedin,
} = req.body;
const profileFileds = {};
profileFileds.user = req.user.id;
if (company) profileFileds.company = company;
if (website) profileFileds.website = website;
if (location) profileFileds.location = location;
if (bio) profileFileds.bio = bio;
if (status) profileFileds.status = status;
if (githubuername) profileFileds.githubuername = githubuername;
if (skills) {
profileFileds.skills = skills.split(",").map((skill) => skill.trim());
}
//Build profile object
profileFileds.social = {};
if (youtube) profileFileds.social.youtube = youtube;
if (twitter) profileFileds.social.twitter = twitter;
if (facebook) profileFileds.social.facebook = facebook;
if (linkedin) profileFileds.social.linkedin = linkedin;
if (instagram) profileFileds.social.instagram = instagram;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFileds },
{ new: true }
);
return res.json(profile);
}
//Create profile
profile = new Profile(profileFileds);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send("server Error");
}
}
);
I'm trying to post a comment on to my posts for my MERN app but I'm running into an issue where the comment (Posts.findOneAndUpdate) seems to posting the comments twice. I read a few posts on SO that described the issue to be the way mongoose handles queries but I must be missing something.
If anyone could explain what I'm doing wrong I would greatly appreciate it!
Route I'm using:
router.post('/newReply/:id', async function(req, res) {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'No text entered!',
})
}
const reply = new Replies(body)
if (!reply) {
return res.status(400).json({ success: false, error: err })
}
await Posts.findOneAndUpdate(
{ _id: req.params.id },
{
"$inc": { "replies": 1 },
"$push": { "comments": reply },
},
{
new: true
},
(err) => {
if (err) {
return res.status(404).json({
success: false,
error: err,
message: 'Post not found!',
})
}
return res.status(200).json({
success: true,
id: reply._id,
message: 'Reply created!',
reply: reply.reply,
points: reply.points,
createdAt: reply.createdAt
})
})
.catch(err => console.log(err))
})
Posts Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const PostsSchema = new Schema({
post: {
type: String,
required: true
},
points: {
type: Number,
default: 0
},
voters: {
type: Array
},
upvotedBy: {
type: Array
},
downvotedBy: {
type: Array
},
createdAt: {
type: Date,
default: Date.now
},
replies: {
type: Number,
default: 0
},
comments: {
type: Array
},
user_id: {
type: 'string'
},
deleted: {
type: Boolean,
default: false
}
});
module.exports = Posts = mongoose.model("posts", PostsSchema);