update controller fails to works as intended - node.js

I'm trying to make a nodejs\ExpressJs backend for a small project. The backend is split into services, controllers and router files. While testing the updateProduct (to update the fields of a product that already exists in database) function on postman using a x-www-form-urlencoded body, I got the following error:
{
"err": {
"stringValue": "\"{ name: 'Hp', description: 'Pavillion', price: '20000' }\"",
"valueType": "Object",
"kind": "ObjectId",
"value": {
"name": "Hp",
"description": "Pavillion",
"price": "20000"
},
"path": "_id",
"reason": {},
"name": "CastError",
"message": "Cast to ObjectId failed for value \"{ name: 'Hp', description: 'Pavillion', price: '20000' }\" (type Object) at path \"_id\" for model \"Product\""
}
}
Screen of postman form :
This is the code I used in their respective files, I tried logging a message into the console to see whether they are called upon, and yes they were called as intended.
//service file
const updateProduct = async(product)=>{
return await Product.findByIdAndUpdate(product._id, product)
}
//controller file
const updateProduct = async(req, res)=>{
try{
const result = await productService.updateProduct(req.body)
res.status(200).json(result)
}
catch(error){
res.status(500).json({err:error})
}
}
//router file
router.put("/:id", productController.updateProduct)
I tried changing the code in the service layer to the following, it didn't work, but it not generate the same problem.
const updateProduct = async(product) => {
return await Product.findByIdAndUpdate(
product._id,
{
$set: {
name: product.name,
price: product.price,
description: product.description
}
},
{ new: true }
);
};
If relevant I'm using Mongoose and other methods such as adding or deleting work as intended. Thanks

Problem:
You have to get the _id of the desired product from the params not from the req.body which return null like:
this:
return await Product.findByIdAndUpdate(product._id, product);
would turn into:
return await Product.findByIdAndUpdate(undefined, {
"name": "Hp",
"description": "Pavillion",
"price": "20000"
})
and of course, the record won't get updated.
Solution:
your controller should look like this (I've used the path):
//controller file
const updateProduct = async(req, res)=>{
try{
const result = await productService.updateProduct(req.params.id, req.body)
res.status(200).json(result)
}
catch(error){
res.status(500).json({err:error})
}
}
and your service should look like this:
//service file
const updateProduct = async(id, product)=>{
return await Product.findByIdAndUpdate(id, product)
}
``

Related

Mongoose problem with object id "Cast to ObjectId failed for value"

It's along explanation so to make it easier I'll call users x and y.
I got this code, which the intention is to the X (making the requests) add his ID into Y's pending requests as well as his own sent requests.
const postSendBBRequest = async (req, res) => {
const userRecipient = await User.findById(req.params.id)
const userSender = await User.findById(req.userId);
const senderId = userSender.id;
try {
if (senderId == userRecipient.id) {
res.status(406).json("You cannot have yourself as a brother")
} else {
userSender.sentBBRequests.push({senderId});
await userSender.save();
userRecipient.receivedBBRequests.push({senderId});
await userRecipient.save();
res.status(201).json("Brotheband request sent sucessfully")
}
} catch (ERR) {
res.status(500).json({ Message: ERR.message });
}
}
My test route on Postman is /add/61b29bb33e775393ae369b79
The problem is: I'm getting an error: "Cast to ObjectId failed for value \"{ senderId: '61b29aef3e775393ae369b74' }\" (type Object) at path \"sentBBRequests\
I thought maybe the problem was how I organized my Schema too:
receivedBBRequests: [
{
type: mongoose.Schema.Types.ObjectId,
},
],
sentBBRequests: [
{
type: mongoose.Schema.Types.ObjectId,
},
],
brothers: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
},
]}
There are too many points of failure I can't even come up with something to solve.
Thanks a lot.
You can use the following :
const senderId = userSender._id.toString();
const userRecipientId = userRecipient._id.toString();
this will allow you to convert the objectId to string

How I can post Request Body array to MongoDb using Nodejs and Mongoose

I am trying the following things:
1 Getting request body (Which is embedded with an Array).
2 Then After getting the request body I am trying to save all the objects in the array into my key of schema called "ExtraInfo".But When I run the API My "ExtraInfo" key has only object id without data.
I have Explained my whole question with my code and in my API Code, I have mentioned my logic with a comment where I am trying to push my array data to my "ExtraInfo" key.
Model:
const mongoose = require('mongoose');
const ExtraInfoModel = mongoose.Schema({
title:String,
body:String,
date: Date
})
const EmbeddedDataModel = mongoose.Schema({
class:Number,
ExtraInfo:[ExtraInfoModel],
})
module.exports = mongoose.model('EmbeddedDataCluster',EmbeddedDataModel);
RequestBody:
{
"class":"96",
"ExtraInfo":[
{
"title":"new1",
"body":"dummy1"
},
{
"title":"new2",
"body":"dummy2"
},
{
"title":"new3",
"body":"dummy3"
},
{
"title":"new4",
"body":"dummy4"
},
{
"title":"new5",
"body":"dummy5"
}
]
}
Now My Api
Router.post('/Embed', async (req, res) => {
const _ExtraInfo = await req.body.ExtraInfo;
console.log(_ExtraInfo);
try {
const _EmbeddedDataModel = await new EmbeddedDataModel({
class: req.body.class,
ExtraInfo:{$push:{$in:_ExtraInfo}}//Here is trying to loop through
//each object from array and trying to push in Key But when the Data
// saved the "ExtraInfo" key is empty and only have object id
});
_EmbeddedDataModel.save();
console.log(_EmbeddedDataModel);
res.json(_EmbeddedDataModel);
} catch (error) {
console.log(error);
res.json(error);
}
})
Result of DB:
{
"_id": "601a1f0e9fcda33570f9e26b",
"class": 96,
"ExtraInfo": [
{
"_id": "601a1f0e9fcda33570f9e26c"
}
]
}
Please Help me

How can I add multiple object references prior to saving a record using Mongoose and Node?

I am saving a record to my MongoDB database and am encountering a multiple save error when I attempt to add multiple object references to the record.
I have tried to change the way my code handles promises with .then and async/await functions but I do not think I am addressing the root cause of the problem.
Here is the code I am working with.
MENU SCHEMA
const MenuSchema = new Schema({
description: String,
title: String,
notes: String,
_menuItems: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "menuItems"
}
]
});
mongoose.model("menus", MenuSchema);
module.export = MenuSchema;
MENU ITEM SCHEMA
const MenuItemSchema = new Schema({
imageURL: String,
name_en: String,
name_es: String,
type_en: String,
type_es: String,
description_en: String,
description_es: String,
dietaryCallouts: [String],
price: Number
});
mongoose.model("menuItems", MenuItemSchema);
module.export = MenuItemSchema;
MENU CONTROLLER
create(req, res, next) {
try {
const { description, title, notes } = req.body;
const { _menuItems } = req.body;
const menu = new Menu({
description,
title,
notes,
_menuItems: []
});
let counter = 0;
_menuItems.forEach(function(i) {
counter++;
MenuItem.findOne({ _id: i }).then(menuItem => {
menu._menuItems.push(menuItem);
if (counter === _menuItems.length) {
menu.save().then(menu => {
res.send(menu);
});
}
});
});
} catch (err) {
res.status(422).send(err);
}
}
The expected result of the code is to loop through all of the menuItems ids, find them in their respective model, push them into the _menuItems array, and after all items have been added, save the menu object.
The error message that I receive is: "ParallelSaveError: Can't save() the same doc multiple times in parallel."
The object is saved, but only 1 of the menu items have been saved to the database instead of all of them.
For some reason the save function is getting called multiple times even though the logic flow should only execute once.
Any help would be much appreciated!
As I understand, you are trying to create a new menu with the given menuItems.
So you do it simply like this: (there is no need to loop)
router.post("/create-menu-and-items", async (req, res, next) => {
try {
const { description, title, notes, _menuItems } = req.body;
let menu = new Menu({ description, title, notes, _menuItems });
menu = await menu.save();
res.status(201).send(menu);
} catch (err) {
console.log(err);
res.status(422).send(err);
}
});
With this when you post to this url, response will be like this ( I assume you will send the existing menu item ids in the body:
{
"_menuItems": [
"5dc81674ed5a8a3f78356779",
"5dc81680ed5a8a3f7835677a",
"5dc8168bed5a8a3f7835677b"
],
"_id": "5dc8180990b714224462fb2b",
"description": "Menu 1 description",
"title": "Menu 1 title",
"notes": "Menu 1 notes",
"__v": 0
}
This will be extra info, but if in the future you want to add menu items to an existing menu, you can do like this:
router.post("/add-menu-items-to-menu/:id", async (req, res, next) => {
try {
const { _menuItems } = req.body;
let menu = await Menu.findByIdAndUpdate(req.params.id, { $push: { _menuItems } }, { new: true });
if (menu) {
res.status(200).send(menu);
} else {
res.status(400).send("Menu not found for this id");
}
} catch (err) {
console.log(err);
res.status(422).send(err);
}
});
Body:
{
"_menuItems": ["5dc81694ed5a8a3f7835677c", "5dc816a5ed5a8a3f7835677d"]
}
Response:
{
"_menuItems": [
"5dc81674ed5a8a3f78356779",
"5dc81680ed5a8a3f7835677a",
"5dc8168bed5a8a3f7835677b",
"5dc81694ed5a8a3f7835677c",
"5dc816a5ed5a8a3f7835677d"
],
"_id": "5dc81b134c92ae2884c2468c",
"description": "Menu 1 description",
"title": "Menu 1 title",
"notes": "Menu 1 notes",
"__v": 0
}

Cyclic schema giving error as "Error: Schema must contain unique named types but contains multiple types named "Node"."

I am new to 'GraphQL' using nodejs. I am stucked into bi-directional schema mapping. posts <-> authors. Using graphql and graphql-relay module.
Following are the two schema we are using.
--posts.js // here we are requiring authors.js
const {
AuthorType,
schema: AuthorSchema,
AuthorsConnection
} = require('./authors');
class Post {}
const {
nodeInterface,
nodeField
} = nodeDefinitions(
globalId => {
const {
type,
id
} = fromGlobalId(globalId);
// return based on the id
return DataSource['posts'][0];
},
obj => {
console.log(" : PostType : ", PostType);
// type to be return
return Post;
}
);
const PostType = new GraphQLObjectType({
"name": "PostType",
"description": "Posts type and it's relevant fields",
"fields": () => ({
"id": globalIdField('Post'),
"title": {
"type": GraphQLString
},
"body": {
"type": GraphQLString
},
"author": {
"type": AuthorsConnection,
"resolve": (parent, argument, root, currentSdl) => {
console.log("v1, v2, v3, v4 :", parent);
if (parent.author)
return connectionFromArray(DataSource['authors'], {})
return [];
}
}
}),
isTypeOf: Post,
interfaces: [nodeInterface]
});
const {
connectionType: PostsConnection,
edgeType: GQLPostEdge
} = connectionDefinitions({
name: "Post",
nodeType: PostType
});
module.exports = exports = {
PostType,
PostsConnection,
schema: {
post: nodeField,
posts: {
type: PostsConnection,
resolve: (root, v2, v3) => {
return connectionFromArray(DataSource['posts'], {});
}
}
}
};
--authors.js // here we have required posts.js
const {
PostType,
PostsConnection
} = require('./posts');
class Author {}
const {
nodeInterface,
nodeField
} = nodeDefinitions(
globalId => {
const {
type,
id
} = fromGlobalId(globalId);
// return based on the id
return DataSource['authors'][0];
},
obj => {
console.log(" : Authorype : ", Authorype);
// type to be return
return Author;
}
);
const AuthorType = new GraphQLObjectType({
"name": "AuthorType",
"description": "Author type and it's relevant fields",
"fields": () => ({
"id": globalIdField('Author'),
"firstName": {
"type": GraphQLString
},
"lastName": {
"type": GraphQLString
},
authorPosts: {
type: PostsConnection,
resolve: (parent, args, root, context) => {
return connectionFromArray(DataSource['posts'], {});
}
}
}),
isTypeOf: null,
interfaces: [nodeInterface]
});
const {
connectionType: AuthorsConnection,
edgeType: GQLAuthorEdge
} = connectionDefinitions({
name: "Author",
nodeType: AuthorType
});
module.exports = exports = {
AuthorType,
AuthorsConnection,
schema: {
author: nodeField,
authors: {
type: AuthorsConnection,
resolve: (root, v2, v3) => {
return connectionFromArray(DataSource['authors'], {});
}
}
}
};
Once I merge above schema for GraphQL I am getting following error.
Error: Schema must contain unique named types but contains multiple types named "Node".
I tried to debugged this issue, following is I observed following.
Once I change "authors" field from posts schema to other than
"AuthorsConnection" it starts working.
Or if removed "authors" field
from posts schema it starts working.
Please let me know what is issue here, is it relevant to nodeDefinitions function?
It is indeed related to the nodeDefinitions function. From the graphql-relay docs:
nodeDefinitions returns the Node interface that objects can implement, and returns the node root field to include on the query type. To implement this, it takes a function to resolve an ID to an object, and to determine the type of a given object.
You're calling this twice, which is resulting in the Node type being defined twice, and you're referencing one of each:
schema: {
post: nodeField,
// ...
schema: {
author: nodeField,
This is causing the error - there's now two independent instances of Node which is invalid.
The solution is to only call nodeDefinitions once, and then pass the reference to the generated nodeField and nodeInterface to the relevant places. Then your globalId => {...} function will need to look at the type to figure out how to get the relevant record, be it an author or a post.
Along with above answer given by #Benjie. I find out the way to overcome issues which was resulting into error of Error: Schema must contain unique named types but contains multiple types named "Node"..
Following are the key points to be check when we are making graphql in modular way.
Don't create new instances of type, For eg: const PostType = new GraphQLObjectType({}) it should always send single object rather than new object every time.
use nodeDefinations only once.
Check for the cyclic issues in common javascript issue which will occurs.
Thanks.

Deleting a single item from a database with mongodb

{
"_id": {
"$oid": "5a2de0a00d6baa43e8b925d0"
},
"name": "test",
"playList": [
{
"url": "https://p.scdn.co/mp3-preview/8aa799e60164f8a1fb311188d9d85ef65d7782c6?cid=ed36a056ee504173a3889b2e55cbd461",
"artist": "Kenny G",
"songName": "My Heart Will Go On (Love Theme from \"Titanic\")",
"_id": {
"$oid": "5a2de0ad0d6baa43e8b925d1"
}
},
{
"url": "https://p.scdn.co/mp3-preview/7c49854f18e6dfda6cd97ab5e8bc139d7ca82b7c?cid=ed36a056ee504173a3889b2e55cbd461",
"artist": "PRODUCE 101",
"songName": "PICK ME",
"_id": {
"$oid": "5a2de13b0d6baa43e8b925d2"
}
}
],
"__v": 0
}
I have a database called channels where each channels contain a playList as shown below. I want to delete a single item when a button is clicked. I can handle the onClick event part, but I am not sure how to implement the routes part.
I know that I start by doing something like
router.delete(''/channels/:id', function(req, res){
something here...
})
but how can I access a particular item (probably with a unique id?) and delete it from the DB?
EDIT
By using the GET below
router.get('/channels/:id',
isLoggedIn,
function(req, res) {
channel.findOne({'name':req.params.id},function(err,channeldata){
if(err || channeldata === null){
res.status(404).send({
message: 'Channel Not Found',
data: []
})
}
else {
res.status(200).json({
message: "channel to "+req.params.id+"success",
data:channeldata
})
}
})
});
I get the data for a single channel in my DB.
But since I am new to this stuff, I am not sure how to access each item of the playList and delete a single data.
EDIT2
var mongoose = require('mongoose');
var ChannelSchema = new mongoose.Schema({
name: {type:String,required:true},
playList: [{
songName: { type : String },
artist: { type : String },
url: { type : String }
}]
})
module.exports = mongoose.model('Channel',ChannelSchema);
You can try the following snippet that contains the DELETE (part of CRUD) endpoint for your resource collection (i.e. the channels):
router.delete('/channels/playlist/song', isLoggedIn, (req, res) => {
const channel_id = req.query.channelId;
const song_id = req.query.songId;
// the following query deletes a song form a playlist of a certain channel
channel.update({_id: ObjectId(channel_id)},{$pull:{playList:{_id:ObjectId(song_id)}}})
.exec()
.then(result => {
// for checking if document was found and deleted
// mongodb actually returns special object `result`
// which has its own certain fields
res.status(200).send({
status: "success",
message: result
});
})
.catch(error => {
// here we see if we had any problem with server or db itself
console.log(error)
res.status(500).send({
success: false,
message: "Something went wrong with DELETE /channels/:id"
})
})
});
I assume that you know what ObjectId() function does
if you do not have it declared, declare the following comment
in the beginning of the file (where you require everything)
const mongoose = require('mongoose'); // you must have this
const ObjectId = mongoose.Types.ObjectId; // gets the function
Let me know if this helps, or if you do not understand something - I will make an edit so that you get it.

Resources