why unique : true schema validation is not working in mongoose? - node.js

here when I try to insert the data with proper validation it works. and when I post with wrong validation it also throws error as expected but when I use the email that was already present in my database and i had set the unique:true but it stores the document with same email.
What am i doing wrong
My model file:
const mongoose = require("mongoose")
const Userschema = new mongoose.Schema({
name:{
type:String,
required:true
},
email:{
type:String,
required:true,
unique:true,
},
password:{
type:String,
required:true
},
date:{
type:Date,
default:Date.now
}
})
const User = new mongoose.model("user", Userschema)
module.exports = User
main file:
const express = require("express");
const router = express.Router();
const User = require("../models/User");
const { body, validationResult } = require("express-validator");
router.post(
"/createuser",
[
body("name", "please enter valid name").isLength({ min: 5, max: 15 }),
body("password", "your password must be larger or equalt to 8 characters").isLength({ min: 8 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const data = await User.create(req.body);
res.json(data);
} catch (error) {
console.log(error);
res.send("some error occured")
}
}
);

The code is correct, If you add the unique after running the API to save the user in DB. It will not work. Because unique indexing runs the first time while creating collections. No worry, drop the data and run API. It should be working. If you don't want to drop the DB. The other solution is to write a migration task to create a unique index.
const { MongoClient } = require('mongodb');
const createIndex = async () => {
try {
const url = 'paste your db url';
const client = new MongoClient(url);
const dbName = 'paste your db name';
await client.connect();
const db = client.db(dbName);
await db.collection('users').createIndex(
{email: 1 },
{ unique: true },
);
process.exit(1);
} catch (err) {
console.log(err.stack);
}
};
createIndex();
Run the above code with 'node'.

Related

Data is not stored in mongoDB database - node.js

I write node.js code and try to insert data into a mongodb database by Postman (post) but the data is not saved, only _id and _v are inserted into the documents ,that are entered automatically. I think the problem is in the body-parser, it is deprecated, but I tried a few options and it remained deprecated. (I'm not sure the problem with bodyparser).The data were inserteded via Postman(post). This is the relevant node.js code:
const express = require ('express');
const mongoose = require ('mongoose');
const router = require ('./routes/api');
const bodyParser = require("body-parser");
const dotenv = require('dotenv');
dotenv.config();
const app = express();
.
.
.
app.use(bodyParser.json());//this bodyParser is deprecated
app.use('/',router);
model:
const mongoose = require ('mongoose');
const userSchema = mongoose.Schema({
name:{
type:String,
require
},
password:{
type:String,
minlength:8,
require
}
})
module.exports = mongoose.model('User', userSchema);
controller:
const User = require ('../models/User');
const newUser = async (req,res)=>{
let user1 = new User(
req.body);
console.log(`${user1} added`);
try{
await user1.save();
res.status(200).json({newUser:user1});
}
catch(error){
res.send(`cant save new user: ${error.message}`)
}
}
module.exports = { newUser }
This is what I wrote in Postman:
{
"name":"james",
"password":"12345678"
}
and this is the response:
{
"newUser": {
"_id": "60a68f815019f31cfc098572",
"__v": 0
}
}
I would be very happy to get help !!
Please make the following changes to get the desired result.
// In place of app.use(bodyParser.json()), use
app.use(express.json())
// Controller
const userSchema = require("../models/User");
const newUser = async (req, res) => {
let user1 = new userSchema({
name: req.body.name,
password: req.body.password,
});
console.log(`${user1} added`);
try {
await user1.save().then(() => res.status(201).json({ newUser: user1 }));
} catch (error) {
res.send(`cant save new user: ${error.message}`);
}
};
module.exports = { newUser };
Also, inside the user schema, please change require to required: true.
// Model
const userSchema = new mongoose.Schema({
name:{
type: String,
required: true
},
password:{
type: String,
minLength:8,
required: true
}
})
Modify your model.js file. It will check if anything is missing in req.body
const mongoose = require ('mongoose');
const userSchema = mongoose.Schema({
name:{
type:String,
required: true
},
password:{
type:String,
minlength:8,
required: true
}
})
module.exports = mongoose.model('User', userSchema);

Cannot overwrite `User` model once compiled. When resolved i get 'User' Undefined

I'm currently working on the backend of a shopping website,
while working on my post functions. I get this error
"OverwriteModelError: Cannot overwrite User model once compiled."
Everything works fine until when i go for the buy character where it needs a search query for both the User and Character and pushing the character into the array ownedCharacter in the User model.
when i remove the const User = require('../Model/User'); and test the function buy character in postman it says that the User is Undefined.
Here's the code for the model
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username:{
type: String,
required : true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true,
min: 8
},
status:{
type:Boolean,
default:false
},
points:{
type:Number,
default: 0
},
rank:{
type:String,
default:"unranked"
},
avatar:{
type:String,
default:""
},
level:{
type:Number,
default:0
},
experience:{
type:Number,
default:0
},
registrationDate:{
type:Date,
default:Date.now()
},
gold:{
type:Number,
default:0
},
gems:{
type:Number,
default:0
},
ownedCharacter:[{
type:mongoose.Schema.Types.ObjectId,
ref:"Character",
}],
},{timestamps:true});
module.exports = mongoose.model("User", UserSchema);
Here's the code for the app.js
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static('uploads'));
//Import Routes
const authRoute = require ('./Route/Auth');
const authAdminRoute = require ('./Route/Admin_Auth');
const shopRoute = require('./Route/Shop');
dotenv.config();
//Connection to DataBase
mongoose.connect(
process.env.DB_CONNECT,
{useNewUrlParser: true, useUnifiedTopology: true},
()=>console.log('connected to database')
);
//MiddleWares
app.use(express.json());
//Route MiddleWares
app.use('/api/user', authRoute);
app.use('/api/admin', authAdminRoute);
app.use('/api/shop',shopRoute);
//Server Listener
app.listen(27017, ()=> console.log("Server Running"));
and here's the shop route
const router = require('express').Router();
const User = require('../Model/User');
const Character = require('../Model/Character');
const Skin = require('../Model/Skin');
//admin add character
router.post('/addCharacter', async (req, res) =>{
const character = new Character({
name:req.body.name,
price:req.body.price,
story:req.body.story,
ability:{
abilityName:req.body.abilityName,
cooldown:req.body.cooldown,
description:req.body.description
},
});
try {
const savedCharacter = await character.save();
res.json({savedCharacter});
console.log(character);
} catch (error) {
}
});
//add Skin
router.post('/addSkin/:characterId', async (req, res) =>{
const skin = new Skin({
name:req.body.name,
price:req.body.price,
description:req.body.description,
characterId: req.params.characterId
});
try {
const savedSkin = await skin.save();
res.json({skin});
console.log(skin);
} catch (error) {
}
});
//add Monsters
router.post('/addMonster/', async (req, res) =>{
const mosnter = new Monster({
name:req.body.name,
price:req.body.price,
description:req.body.description,
});
try {
const savedMonster = await monster.save();
res.json({monster});
console.log(monster);
} catch (error) {
}
});
//add Traps
router.post('/addTrap', async (req, res) =>{
const trap = new Trap({
name:req.body.name,
price:req.body.price,
description:req.body.description,
});
try {
const savedTrap = await trap.save();
res.json({trap});
console.log(trap);
} catch (error) {
}
});
//buy a Character
router.post('/buyCharacter/:characterId/:userId',async(req,res)=>{
try {
const user = await User.findOne({_id:req.params.userId});
console.log("user:"+user);
const boughtCharacter = await Character.findOne({_id:req.params.characterId});
user.ownedCharacter.push(boughtCharacter);
user.save();
res.json(boughtCharacter);
console.log(boughtCharacter);
} catch (error) {
console.log(error.message);
}
});
module.exports = router;
Is the casing correctly matched between your require statements and the file names? If the filename is actually /Model/user.js, it could throw this error.
The error message is telling you that somehow, you are declaring a model named User more than once. Unless you are explicitly doing so again in another file, you're best bet is that the User.js file is being loaded twice. There are a few reasons why that can happen, but the most likely cause is that you are importing the ../Model/User.js file in multiple places, but your casing is inconsistent.
When Node loads an import, it caches it using the resolved filename you pass to require()... and it does so in a case-sensitive manner. What that means is that if you have require(../Model/user) in one file and require(../Model/User) in
another, Node will load the same file twice, thus executing the file twice, thus attempting to set a model named User on the shared mongoose instance twice, and thus giving you the OverwriteModelError you are experiencing.
I'd do a find in your IDE and look at everywhere you are requiring that model.

i am trying to add multiple users on a atlas mongoDB

I have created a rest api and I am trying to add multiple users to atlas mongodb I use this schema
const mongoose = require('mongoose');
const { v1: uuidv1 } = require('uuid');
const crypto = require('crypto')
const userSchema = new mongoose.Schema({
// _id: mongoose.Types.ObjectId,
name: {
type: String,
// trim: true,
unique: true,
required: true,
index: true
},
email: {
type: String,
// trim: true,
required: true,
unique: true,
},
hashed_password: {
type: String,
trim: true,
required: true
},
salt: String,
created: {
type: Date,
default: Date.now
},
updated: Date,
})
// VIRTUAL FIELD
userSchema.virtual('password')
.set(function(password){
//create temporary variable called _password
this._password = password
//generate a timestamp
this.salt = uuidv1();
//encryptPassword
this.hashed_password = this.encryptPassword(password)
})
.get(function(){
return this._password
})
///methods
userSchema.methods = {
authenticate: function(plainText){
return this.encryptPassword(plainText) === this.hashed_password
},
encryptPassword : function(password){
if(!password) return "";
try{
return crypto.createHmac('sha256', this.salt)
.update(password)
.digest('hex');
} catch(err){
return ""
}
}
}
module.exports = mongoose.model('User', userSchema);
I use this function to sign up :
exports.signup = async (req, res) => {
const userExists = await User.findOne({email : req.body.email})
if(userExists) return res.status(403).json({
error: "EMAIL is TAKEN"
})
const user = await new User(req.body)
await user.save()
.then(result => {res.json({result: result})})
.catch(err => res.json({err : err}))
}
I validate :
exports.userSignupValidator = (req, res, next) => {
//name is not null and its between 4 and 10 characters
req.check('name', 'name is required').notEmpty();
//email is not null, valid and NORMALIZED -> we will use method chaining
req.check('email', 'please enter valid email')
.matches(/.+\#.+\..+/)
.withMessage('email must contain #')
.isLength({
min: 4,
max: 2000
})
//check for password
req.check('password', 'Password is required').notEmpty();
req.check('password').isLength({
min: 6,
}).withMessage('password must be minimum 6 char long').matches(/\d/).withMessage('must contain a number')
//check for errors
const error = req.validationErrors()
////////if error apears show the first one as they appear
if(error){
const firstError = error.map((error) => error.msg)[0]
return res.status(400).json({error: firstError})
}
////proceed to next middleware
next()
}
and I use the route :
const express = require('express'); //bring in express
const postController = require('../controlers/postControler') //brings everything that is exported from the postControler FILE and becomes a OBJECT
const router = express.Router();
const validator = require('../validator');
const signup = require('../controlers/authControler');
const userById = require('../controlers/userControler');
router.get('/', postController.getPosts)
router.post('/post', signup.requireSignIn, validator.createPostValidator, postController.createPost)
router.get('/test' , postController.test)
router.post('/signup', validator.userSignupValidator, signup.signup)
router.post('/signin', signup.signin)
router.get('/signout', signup.signout)
router.get('/lahoha', userById.getUsers)
////find the user by id with params
////any routes containing :userId our app will first execute userById()
router.param('userId', userById.userById);
///////////////////////////////////////////////
module.exports = router
the problem is when I try to create a second user with postman with :
{
"name": "petru",
"email": "petru#gmail.com",
"password": "notazece10"
}
I get the error :
{
"err": {
"driver": true,
"name": "MongoError",
"index": 0,
"code": 11000,
"keyPattern": {
"username": 1
},
"keyValue": {
"username": null
}
}
}
Please help !!!!! this error is driving me crazy, I don't know what I'm doing wrong
after running thru my code multiple times line by line i found out the code is fine , the problem was in my atlas mongodb database.
So i am new to nodejs and mongo , and i try to learn , when i created my first mongodb database in atlas i did not pay attention to naming my database so it had the default name of .
I went back to atlas mongodb and i made a new database ( cluster) , named it TEST , copied the link, went into my dotenv file paste the link to my MONGO_URI restarted the server and then all code worked fine now i can add as many users as i want.
I hope other newcomers to mongodb and nodejs learn from my mistake and if someone ever repeats my STUPID mistake i hope they find this and fix it.

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory node.js

Basically, I try to post formdata to my own api. I think I have got a issue with array of objects.
const handleSubmit = async e => {
e.preventDefault()
try{
const formData = new FormData()
formData.append('title',formState.inputs.title.value)
formData.append('image',formState.inputs.image.value)
formData.append('ingredients',JSON.stringify(ingredientData))
formData.append("instructions",JSON.stringify(instructionData))
formData.append('readyInMinutes',formState.inputs.readyInMinutes.value)
formData.append('servings',formState.inputs.servings.value)
formData.append('price',formState.inputs.price.value)
formData.append('creator',auth.userId)
const responseData = await axios.post(
process.env.REACT_APP_BACKEND_URL+'/recipes/new',
formData,{
headers: {Authorization : `Bearer ${auth.token}`} })
console.log(responseData)
}
catch(err){
console.log(err.message)
}
}
I can send this to my own api. My server refused to send response.
This recipe controller.js
const createRecipe = async (req, res, next) =>{ //We create a new recipe
const errors = validationResult(req)
if(!errors.isEmpty()){
const error = new HttpError('Invalid inputs passed, please check your data. 2',422)
return next(error)
}
const {title,ingredients,instructions,readyInMinutes,servings, ratings,comments, nutrients,price} = req.body
console.log(req.file)
let newIngredients = JSON.parse(ingredients)
let newInstructions = JSON.parse(instructions)
const myIngredients = []
let myInstructions = []
console.log(newIngredients)
console.log(newInstructions)
for(let i = 0;newIngredients.length;i++){
let createIngredient = new Ingredient({
name:newIngredients.name,
amount:newIngredients.amount,
measure:newIngredients.measure
})
myIngredients.push(createIngredient)
}
for(let i = 0;i<newInstructions.length;i++){
let createInstruction = new Instruction({
content:newInstructions.content
})
myInstructions.push(createInstruction)
}
console.log(myInstructions)
console.log(myIngredients)
const createdRecipe = new Recipe({
title,
image:req.file.path,
ingredients:myIngredients,
instructions:myInstructions,
readyInMinutes,
servings,
price,
creator:req.userData.userId,
ratings:[],
comments:[],
nutrients:[],
})
let user
try{
user = await User.findById(req.userData.userId) // When we add a new recipe we need user's recipes array,too.That's why We need user who add this recipe.
}
catch(err){
const errors = new HttpError('Something went wrong',500)
return next(error)
}
if(!user){
const error = new HttpError('This user does not exist',422)
return next(error)
}
try{ // We need to do this.Because When we add a new recipe that affect user's recipes array, too.We want to make sure to add this recipe both collections.
const sess = await mongoose.startSession()
sess.startTransaction()
await createdRecipe.save({session:sess})
user.recipes.push(createdRecipe)
await user.save({session:sess})
await sess.commitTransaction()
}
catch(err){
const error = new HttpError('Created recipe failed, please create again 2',500)
return next(error)
}
res.status(201).json({recipe:createdRecipe})
}
I get those arrays and apply Json.parse() to convert json string to js object.
Recipe model
const mongoose = require('mongoose')
const uniqueValidator = require('mongoose-unique-validator')
mongoose.set('useCreateIndex', true);
const ingredientSchema = require("./Ingredient").schema;
const instructionSchema = require("./Instruction").schema;
const commentSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
content: {
type: String,
required: true,
maxLength: 280
}
}, {
timestamps: true, // this adds `createdAt` and `updatedAt` properties
toJSON: {
// whenever the comment is converted to JSON
transform(doc, json) {
delete json.__v
return json
}
}
})
const recipeSchema = new mongoose.Schema({
title:{
type:String,
required:true
},
image:{
type:String,
required:true
},
ingredients:[ingredientSchema],
instructions:[instructionSchema],
readyInMinutes:{
type:Number,
required:true
},
servings:{
type:Number,
required:true
},
price:{
type:Number,
required:true
},
creator:{
type:mongoose.Types.ObjectId,
required:true,
ref:'User'
},
ratings:[{
point:{
type:Number,
required:true
}
}],
comments:[commentSchema],
nutrients:[{
name:{
type:String,
required:true
},
amount:{
type:Number,
required:true
}
}],
})
recipeSchema.plugin(uniqueValidator) //We plugin wiht mogooseValidator with our schema.
module.exports = mongoose.model('Recipe',recipeSchema) //We called User model with recipeSchema
I use mongodb for database.
I did silly mistakes in for loop :)
for(let i = 0;newIngredients.length;i++){
let createIngredient = new Ingredient({
name:newIngredients.name,
amount:newIngredients.amount,
measure:newIngredients.measure
})

Mongoose model.create() responds empty or do not responde

I created a small project with node+express+mongodb
This is where i call the create user:
const express = require("express");
const User = require("../models/user");
const router = express.Router();
router.post("/register", async (req, res) => {
try {
const user = await User.create(req.body);
return res.send({ user });
} catch (e) {
return res.status(400).send({ error: "Registration failed" });
}
});
module.exports = app => app.use("/auth", router);
and here is the Schema for the user:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
name: {
type: String,
require: true
},
email: {
type: String,
require: true,
unique: true,
lowercase: true
},
password: {
type: String,
require: true,
select: false
},
createdAt: {
type: Date,
default: Date.now
}
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
But when o make the resquest, it nevers get a response, it waits forever. And when o take out the await from the request, it gets an empty response { "user": {}}
I'm kind lost looking the mongoose documentation (the ideia is to make a simple rest api, i'm used with python, but looking to learn node)
You have to create a new user from User Model as follows:
const express = require("express");
const User = require("../models/user");
const router = express.Router();
router.post("/register", async (req, res) => {
try {
var user = new User(request.body);
var result = await user.create();
return res.send({ result });
} catch (e) {
return res.status(400).send({ error: "Registration failed" });
}
});
module.exports = app => app.use("/auth", router);

Resources