MongoDB & Mongoose, Save Nested Array of Objects - Node.js - node.js

I'm trying to save a nested array of objects to my MongoDB collection but the app is only saving the first object in the nested BankAccountsArray, I've tried using the .markModified() method but haven't got any success. I've attached the data accepted from the frontend part + my model schema + the route, Thanks for the help!
Frontend Data Accepted:
{
companyHoldingPercentage: '10%',
BankAccountsArray: [
{
bankAccountNumber: '32',
bankBranchNumber: '55',
bankName: 'abc'
},
{
bankAccountNumber: '3123',
bankBranchNumber: '412',
bankName: 'cde'
}
]
}
Model:
const mongoose = require("mongoose");
const BankAccountsArraySchema = mongoose.Schema({
bankName: String ,
bankBranchNumber: String ,
bankAccountNumber: String
}
);
const BankAccountsIndexSchema = mongoose.Schema({
companyHoldingPercentage: String ,
BankAccountsArray: [BankAccountsArraySchema]
});
module.exports = mongoose.model(
"bank-accounts-object",
BankAccountsIndexSchema
);
Route:
var express = require("express");
var router = express.Router();
const BankAccountsIndexModel = require("../Models/BankAccountsIndexModel");
router
.route("/BankAccountsIndexRoute")
.get(async (req, res, next) => {
BankAccountsIndexModel.find((err, collection) => {
if (err) {
console.log(err);
} else {
res.json(collection);
}
});
})
.post(async (req, res, next) => {
console.log(req.body);
const {
companyHoldingPercentage,
BankAccountsArray: [{ bankName, bankBranchNumber, bankAccountNumber }],
} = req.body;
try {
const NewBankAccountsIndexModel = new BankAccountsIndexModel({
companyHoldingPercentage,
BankAccountsArray: [{ bankName, bankBranchNumber, bankAccountNumber }],
});
NewBankAccountsIndexModel.markModified(req.body.BankAccountsArray);
NewBankAccountsIndexModel.save();
// res.json(bankAccount);
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
})
module.exports = router;

Can you try to refactor your .post() endpoint like this:
.post(async (req, res, next) => {
try {
let new_bank_account = await BankAccountsIndexModel.create(req.body);
res.status(200).json(new_bank_account);
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
})

Related

node js : test rest API

i'm new learner in backend node js ... in my code below i created an API for questions and it contains get,post,delete and edit
i wanted to test it using the extension rest client in VS code but when i type Get http://localhost:3000/api in route.rest file to test it,it stucks on waiting
is there a way to know if my API works good and can somebody please help me if i have mistake below?
thanks in advance
//server.js
// #ts-nocheck
const express = require('express');
const morgan = require('morgan');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const jwt = require('jsonwebtoken');
const questionRoutes = require('./routes/subscribers');
const cors = require('cors');
const http = require('http');
// Has to be move but later
const multer = require("multer");
const Question = require('./models/subscriber');
// express app
const app = express();
// Explicitly accessing server
const server = http.createServer(app);
// corsfffffffff
app.use(cors());
dotenv.config();
const dbURI = process.env.MONGO_URL || "mongodb://localhost:27017/YourDB";
mongoose.connect(dbURI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(result => server.listen(process.env.PORT || 3000) )
.catch(err => console.log(err));
// register view engine
app.set('view engine', 'ejs');
app.use(express.json);
// middleware & static files
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
app.use(morgan('dev'));
app.use((req, res, next) => {
res.locals.path = req.path;
next();
});
// routes
// question routes
app.use('/questions' , questionRoutes );
// 404 page
app.use((req, res) => {
res.status(404).render('404', { title: '404' });
});
//questionRoute.js
const express = require('express');
const questionController = require('../controllers/questionCon');
const questionApiController = require('../controllers/questionApiController');
const router = express.Router();
// API Routing
router.get('/api/', questionApiController.get_questions);
router.post('/api/add', questionApiController.create_question);
router.get('/api/:id', questionApiController.get_question);
router.delete('/api/delete/:id', questionApiController.delete_question);
router.put('/api/update/:id', questionApiController.update_question);
// EJS Routing for GUI
router.get('/create', questionController.question_create_get);
router.get('/', questionController.question_index);
router.post('/', questionController.question_create_post);
router.get('/:id', questionController.question_details);
router.delete('/:id', questionController.question_delete);
module.exports = router;
//question.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const questionSchema = new Schema({
questionTitle: {
type: String,
required: true,
},
description: {
type: String,
},
price: {
type: Number,
},
});
const Question = mongoose.model('Question', questionSchema);
module.exports = Question;
//questionAPIcontroller
const Question = require('../models/subscriber');
const validators = require('../validators');
let questionApiController = {
// Get a single question
get_question : async (req , res) => {
const id = req.params.id;
try {
const question = await Question.findById(id,(err, question) => {
if (err) return res.status(400).json({response : err});
res.send("hello")
res.status(200).json({response : question})
console.log("hello")
})
} catch (err) {
res.status(400).json(err);
}
},
// Get all the questions
get_questions: async (req , res) => {
try {
const questions = await Question.find((err, questions) => {
if (err) return res.status(400).json({response : err});
res.status(200).json({response : questions})
})
} catch (err) {
res.status(400).json(err);
}
},
// Create a question
create_question : async (req , res) => {
const {error} = validators.postQuestionValidation(req.body);
if(error) return res.status(400).json({ "response" : error.details[0].message})
try {
const question = await new Question(req.body);
question.save((err, question) => {
if (err) return res.status(400).json({response : err});
res.status(200).json({response : " Question created Successfully"})
});
} catch (err) {
res.status(400).json(err);
}
},
// Delete question
delete_question : async (req , res) => {
const id = req.params.id;
var questionExist = false;
var userId ;
const question = await Question.findById(id).then(result => {
questionExist = true;
userId = result.owner;
}).catch(err => {
questionExist = false;
res.status(400).json({response : err });
});
if(questionExist){
try {
Question.findByIdAndRemove(id ,(err, question) => {
// As always, handle any potential errors:
if (err) return res.json({response : err});
// We'll create a simple object to send back with a message and the id of the document that was removed
// You can really do this however you want, though.
const response = {
message: "Question successfully deleted",
id: question._id
};
return res.status(200).json({response : response });
});
} catch (err) {
res.status(400).json(err);
}
}
else {
return res.status(400).send( { "response" : "A question with that id was not find."});
}
},
// Update question
update_question : async (req , res) => {
const id = req.params.id;
Question.findByIdAndUpdate(id,req.body,
function(err, result) {
if (err) {
res.status(400).json({response : err});
} else {
res.status(200).json({response : "Question Updated"});
console.log(result);
}
})
},
// Get question's questions
}
module.exports = questionApiController
//questionController
const Question = require('../models/subscriber');
const question_index = (req, res) => {
Question.find().sort({ createdAt: -1 })
.then(result => {
res.render('index', { questions: result, title: 'All questions' });
})
.catch(err => {
console.log(err);
});
}
const question_details = (req, res) => {
const id = req.params.id;
Question.findById(id)
.then(result => {
res.render('details', { question: result, title: 'Question Details' });
})
.catch(err => {
console.log(err);
res.render('404', { title: 'Question not found' });
});
}
const question_create_get = (req, res) => {
res.render('create', { title: 'Create a new question' });
}
const question_create_post = (req, res) => {
const question = new Question(req.body);
question.save()
.then(result => {
res.redirect('/questions');
})
.catch(err => {
console.log(err);
});
}
const question_delete = (req, res) => {
const id = req.params.id;
Question.findByIdAndDelete(id)
.then(result => {
res.json({ redirect: '/questions' });
})
.catch(err => {
console.log(err);
});
}
module.exports = {
question_index,
question_details,
question_create_get,
question_create_post,
question_delete
}
change code
app.use(express.json);
to
app.use(express.json());

NodeJs, MongoDb - Why is my code duplicating documents instead of updating them?

I want to find an existing document in MongoDB by its _id. Everything works except trying to edit existing just creates a new document with a new _id instead of updating the current. It seems that it skips if (this._id) { in the model file. Very frustrated because I can see the issue but cannot find a fix. Thanks in advance!
plant.js controller:
exports.getEditPlant = (req, res, next) => {
const editMode = req.query.edit;
if (!editMode) {
console.log('Not Edit Mode');
}
const dbId = req.params._id;
Plant.findById(dbId)
.then(plant => {
if (!plant) {
console.log('Err');
res.redirect('back');
}
res.render('account/edit-plant', {
pageTitle: 'Edit Plant',
path: '/account',
plant: plant,
edit: editMode
});
}).catch(err => {
console.log(err);
});
};
exports.postEditPlant = (req, res, next) => {
const updatedName = req.body.common_name;
const scientific_name = req.body.scientific_name;
const updatedImg = req.body.image_url;
const slug = req.body.slug;
const updatedPlant = new Plant(updatedName, scientific_name, updatedImg, slug, new ObjectId(id));
updatedPlant.addMyPlant()
.then(plant => {
res.redirect('home');
}).catch(err => {
console.log(err);
})
};
plant.js model:
const mongodb = require('mongodb');
const getDB = require('../util/database').getDB;
module.exports = class Plant {
constructor(common_name, scientific_name, image_url, slug, id) {
this.common_name = common_name;
this.scientific_name = scientific_name;
this.image_url = image_url;
this.slug = slug;
this._id = id;
}
addMyPlant() {
const db = getDB();
let dbOp;
if (this._id) {
dbOp = db
.collection('myplants')
.updateOne({ _id: new mongodb.ObjectId(this._id) }, { $set: this });
} else {
dbOp = db.collection('myplants').insertOne(this);
}
return dbOp
.then(result => {
})
.catch(err => {
console.log(err);
});
}
static getMyPlants() {
const db = getDB();
return db.collection('myplants')
.find().toArray()
.then(plants => {
return plants;
}).catch(err => {
console.log(err);
});
}
static findById(dbId) {
const db = getDB();
return db
.collection('myplants')
.find({ _id: new mongodb.ObjectId(dbId) })
.next()
.then(plant => {
return plant;
})
.catch(err => {
console.log(err);
});
}
}
You need to assign the known _id to the ObjectID outside of the query.
In your code, you instantiate a new ObjectID inside your query but the value you passed in is not found.
This is a simplified version of your class with tests:
const mongodb = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;
const url = "mongodb://localhost:27017/mydb";
class Plant {
constructor(name) {
this.name = name;
}
// this method pulls all the records for me to test results
getAll() {
mongodb.connect(url, { useUnifiedTopology: true }, (err, db) => {
if (err) throw err;
var dbo = db.db("mydb");
dbo.collection("plants").find({}).toArray(function (err, result) {
if (err) console.log(err);
console.log(result);
db.close();
});
})
}
// update single record
updateMyPlant() {
mongodb.connect(url, { useUnifiedTopology: true }, (err, db) => {
if (err) console.log(err);
console.log("Mongo connected...");
const dbo = db.db("mydb");
// if you know the _id
// instantiate a variable outside your query
// now const id is the same as the _id you wants to query
const id = ObjectID("5fd8a1100617b03d8c737da0");
const myquery = { "_id": id };
const newvalues = { $set: { "name": "Marlon Brando" }};
try {
dbo.collection("plants").updateOne(myquery, newvalues, (err, res) => {
if (err) console.log(err);
console.log("update result", res.result, res.upsertedId);
db.close();
})
}
catch (e) {
console.log(e);
}
})
}
}
// test
const plant = new Plant("Alexa");
plant.updateMyPlant();
plant.getAll();
module.exports = Plant;
/**
* Logs:
* Mongo connected...
update result {
n: 1,
nModified: 1,
ok: 1
}
null
[{
_id: 5 fd8a1100617b03d8c737da0,
name: 'Marlon Mickey',
scientific_name: 'Brittain'
}]
*/
As you can see the _id is retained while the properties you want to change are updated.
See more info about ObjectID here

Mongoose query between values Express Js

am playing with MongoDb and Mongoose, and I am trying to filter by price range, for example display properties from 0 to any number (10000),
My model is very simple:
const mongoose = require('mongoose');
const PropertySchema = mongoose.Schema({
title: String,
description: String,
price: Number,
town: String
})
module.exports = mongoose.model('Property', PropertySchema);
And route. this what am up to, set price from 0 to any number, I don’t want to hard-code this max value
const express = require('express');
const router = express.Router();
const Property = require('../models/Property');
router.get('/', async (req, res) => {
try {
const properties = await Property.find({ price: { $in: [ 0, 0 ] } });
res.json(properties);
} catch (err) {
res.json({ message: err });
}
});
URL
properties?price=0&10000
Can anybody help me with this? How can I set range of price for properties?
You need to pass both priceMin & priceMax from queryParams & use same in your query
const express = require('express');
const router = express.Router();
const Property = require('../models/Property');
router.get('/', async (req, res) => {
try {
const properties = await Property.find({ price: { $gte:req.query.priceMin, $lte: req.query.priceMax } });
res.json(properties);
} catch (err) {
res.json({ message: err });
}
});
URL
properties?priceMin=0&priceMax=10000
You can do this using $gt and $lt
properties?min=0&max=10000
router.get('/', async (req, res) => {
let min = req.query.min;
let max = req.query.max;
try {
const properties = await Property.find({ price: { $gt : min , $lt : max } });
res.json(properties);
} catch (err) {
res.json({ message: err });
}
});

How do I Solve Express Validator Middleware Error?

First, I have userController like this:
File userController.js
const { validationResults } = require('express-validator');
const { userSignUpValidation } = require('../validator/userSignUpValidate');
function userCreation(req, res) {
try{
const errors = validationResults(req);
if(errors) {
return res.status(400)
.json({
error: {
message: errors.array()[0].msg
}
})
}
bcrypt.hash(req.body.userPassword, saltRounds, function(err, hash) {
User.create({
userId: req.body.userId,
userEmail: req.body.userEmail,
userPhoneNumber: req.body.userPhoneNumber,
userPassword: hash
})
.then((user) => {
return res.status(200).json(user);
})
.catch((error) => {
return res.status(400).json.error;
});
});
} catch(error) {
return res.status(400)
.json({
error: {
message: error
}
})
}
}
In the validator/userSignUpValidate.js the code like this:
'use strict'
const { check } = require('express-validator');
module.exports = [
check('userId').isLength({ min: 5 }).withMessage('The username at least more than 5 characters!'),
check('userPassword').isLength({ min: 6 }).withMessage('The password at least more than 6 characters!'),
check('userPhoneNumber').isLength({ min: 10 }).withMessage('The phone number at least more than 10 characters!'),
check('userEmail').isEmail().withMessage('Is your email correct? Please be correct!')
]
When I test it on postman, the json response always show error by catch on userController.js without error message.
{
"error": {
"message": {}
}
}
My question. To ensure the express-validator run in the right place, where should I put the code?
Firstly you need to import validationResult from express-validator, in your code you are importing validationResults.
Secondly, you are not using your userSignUpValidate middleware. This middleware can be used in controller but it is better to appy it in the userRoute to keep controller clean.
So let's apply userSignUpValidate middleware to the userRoutes.
If the file paths don't match yours, please fix them.
const express = require("express");
const router = express.Router();
const usersController = require("../controllers/userController");
const userSignUpValidate = require("../validator/userSignUpValidate");
router.post("/register", [userSignUpValidate], usersController.userCreation);
module.exports = router;
Then in the controller, we need to import validationResult from express-validator and use it:
const { validationResult } = require("express-validator");
function userCreation(req, res) {
try {
const errors = validationResult(req);
if(!errors.isEmpty()) {
console.log(errors);
return res.status(400).json({
error: {
message: errors.array()[0].msg
}
});
}
//other code you have
} catch (error) {
return res.status(400).json({
error: {
message: error
}
});
}
}
exports.userCreation = userCreation;
When we send a request body with a 3 character password like this:
{
"userId": "userid",
"userPassword": "123",
"userPhoneNumber": "1234567890",
"userEmail": "abc#gmail.com"
}
The response will be like this with a 400 - Bad Request:
{
"error": {
"message": "The password at least more than 6 characters!"
}
}
you should pass like this
const errors = validationResults(req.body);
const Joi = require('#hapi/joi');
const loginValidation = data =>{ const schema = {
username: Joi.string().min(5).required(),
password: Joi.string().min(5).required(),
deviceId: Joi.string().min(2).required()
};
return Joi.validate(data, schema); };
module.exports.loginValidation = loginValidation;
My Routes File
const { registerValidation, loginValidation } = require('../../validation'); router.post('/login', async (req, res)=>{
try {
//validate the data to be submitted
const { error } = loginValidation(req.body);
if (error) return res.status(200).send(
{
status: 0,
message: "Validarion Error",
details: error.details[0].message
});
} catch (error) {
res.status(400).send({
status: 0,
message: "Failed",
error: error
});
}});
this code works fine and is in production mode
Just try like below,
const express = require('express');
const { check, oneOf, validationResult } = require('express-validator');
const app = express();
//this function you need to export in controller
const xxx = (req, res, next) => {
try {
validationResult(req).throw();
res.status(200).send('success');
} catch (err) {
console.log('error messagecame')
res.status(422).json(err);
}
}
app.get('/', oneOf([
check('lang').isIn(['js', 'react', 'angular'])
]), xxx);
app.listen(4000, ()=>{
console.log("running server 4k")
})
Sample working copy
UPDATE 2
I hope below one will help full,
const express = require('express');
const { check, oneOf, validationResult } = require('express-validator');
const app = express();
const xxx = [
check('lang').isIn(['js', 'react', 'angular']),
(req, res, next) => {
try {
validationResult(req).throw();
res.status(200).send('success');
} catch (err) {
console.log('error messagecame')
res.status(422).json(err);
}
}
]
app.get('/', xxx);
app.listen(4000, ()=>{
console.log("running server 4k")
})
SAMPLE WORKING COPY2
Error
https://TruthfulWeirdIrc--five-nine.repl.co?lang=jssddf
Success
https://TruthfulWeirdIrc--five-nine.repl.co?lang=js

Resolving UnhandledPromiseRejectionWarning in express post request

I am trying to make a post request to the server (mongodb) but I get this error:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'todo_description' of undefined
I am running mongodb on my localhost
// Require Express
const express = require("express");
// Setting Express Routes
const router = express.Router();
// Set Up Models
const Todo = require("../models/todo");
// Get All Todos
router.get("/", async (req, res) => {
try {
const todo = await Todo.find();
res.json(todo);
} catch (err) {
res.json({ message: err });
}
});
router.get("/:id", async (req, res) => {
try {
const id = req.params.id;
await Todo.findById(id, (err, todo) => {
res.json(todo);
});
} catch (err) {
res.json({ message: err });
}
});
router.post("/add", async (req, res) => {
const todo = new Todo({
todo_description: req.body.todo_description,
todo_responsible: req.body.todo_responsible,
todo_priority: req.body.todo_priority,
todo_completed: req.body.todo_completed,
});
try {
await todo.save();
res.json(todo);
} catch (err) {
res.json({ message: err });
}
});
router.patch("/update/:id", async (req, res) => {
try {
const updateTodo = await Todo.updateOne(
{ _id: req.params.id },
{ $set: { todo_description: req.body.todo_description } }
);
updateTodo.save().then(updateTodo => {
res.json(updateTodo);
});
} catch (err) {
res.json({ message: err });
}
});
router.delete("/delete/:id", async (req, res) => {
try {
const deleteTodo = await Todo.deleteOne({ _id: req.params.id });
res.json(deleteTodo);
} catch (err) {
res.json({ message: err });
}
});
module.exports = router;
my todo model
// Require Mongoose
const mongoose = require("mongoose");
// Define Schema
// const Schema = new mongoose.Schema;
// Define Todo-Schema
const TodoSchema = new mongoose.Schema({
// Creating Fields
todo_description: {
type: String
},
todo_responsible: {
type: String
},
todo_priority: {
type: String
},
todo_completed: {
type: Boolean
},
todo_date: {
type: Date,
default: Date.now
}
});
// Compile Model From Schema
// const TodoModel = mongoose.model("Todos", TodoSchema);
// Export Model
module.exports = mongoose.model("todos", TodoSchema);
error message:
(node:548) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'todo_description' of undefined
at router.post (C:\Users\kinG\Desktop\projects\mountain-of-prototype\mern\backend\routes\todo.js:33:32)
at Layer.handle [as handle_request] (C:\Users\kinG\Desktop\projects\mountain-of-prototype\mern\backend\node_modules\express\lib\router\layer.js:95:5)
thank you
You are accessing todo_description from req.body. req.body will only be available if you add the body-parser middleware or add a similar one yourself.
Add this right before your routes are loaded :
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
app.use(bodyParser.json());
You can also add this to a specific route. Read more about it here.
You should use body-parser in your master file of the application. Which gives you the parsed json before your middle-ware parse the body, which by-default in string. And also make sure you are sending todo_description in the req.body(should check before use).
const bodyParser = require('body-parser');
app.use(bodyParser.json());

Resources