gets undefined when i call my param in backend node controller - node.js

Here is the request type:
interface IgetProductsByGenderRequest extends express.Request {
readonly params: Readonly<{ gender: string; }>;
}
When I'm using req.params.gender I get undefined.
When I'm using req.params I get the param but in object and I want to get it in a var.
My backend controller:
const getProductsByGender = async (
req: IgetProductsByGenderRequest,
res: IgetProductsByGenderResponse
) => {
console.log(req.params)
ServerGlobal.getInstance().logger.info(
`<getProductsByGender>: Start processing request filtered by and gender ${req.params.gender}`
);
if (
!ServerGlobal.getInstance().isValidGenderValue(+req.params.gender)
) {
ServerGlobal.getInstance().logger.error(
`<getProductsByGender>: Failed to get products because of invalid gender filtered by gender ${req.params.gender}`
);
res.status(400).send({
success: false,
message: "Please provide valid gender",
});
return;
}
try {
const products = await ProductDB.find({ gender: +req.params.gender });
ServerGlobal.getInstance().logger.info(
`<getProductsByGender>: Successfully got the products filtered by gender ${req.params.gender}`
);
res.status(200).send({
success: true,
message: "Successfully retrieved products",
data: products.map((product) => ({
id: product.id as string,
gender: {
value: product.gender,
label: ServerGlobal.getInstance().getGenderLabel(product.gender)!,
},
title: product.title,
description: product.description,
price: product.price,
imageFilename: product.imageFilename,
})),
});
return;
} catch (e) {
ServerGlobal.getInstance().logger.error(
`<getProductsByGender>: Failed to get products filtered by gender ${req.params.gender} because of server error: ${e}`
);
res.status(500).send({
success: false,
message: "Server error",
});
return;
}
};
How can I access the gender param?

I suggest reading the documentation regarding routing and regarding query parameters.
You have req.query, which is for query parameters, e.g. /some/path?myVariable=test would have req.query.myVariable === 'test'.
You also have req.params which is when you're using URL parameters, e.g. /some/path/:id would have req.params.id === 'test' when the user visits /some/path/test.
Make sure you are using/accessing the correct one, as it's easy to make mistakes in this regards. And of course watch out for typos, although you should've spotted that with your console.log(req.params) statement.

Related

Resolve Promise in Nodejs Nested Map Functions and Return Array of Objects From API Call [duplicate]

I'm building an API to add movies to wishlist. I have an endpoint to get all movies in wishlist. My approach was to get the movie ids (not from mongodb) and make an API request to another API to get the movie objects.
This has been successful so far but the problem now is I am getting two objects fused into one object like below:
{
id: 7,
url: 'https://www.tvmaze.com/shows/7/homeland',
name: 'Homeland',
language: 'English',
genres: [ 'Drama', 'Thriller', 'Espionage' ],
status: 'Ended',
runtime: 60,
averageRuntime: 60,
premiered: '2011-10-02',
officialSite: 'http://www.sho.com/sho/homeland/home',
schedule: { time: '21:00', days: [ 'Sunday' ] },
rating: { average: 8.2 },
image: {
medium: 'https://static.tvmaze.com/uploads/images/medium_portrait/230/575652.jpg',
original: 'https://static.tvmaze.com/uploads/images/original_untouched/230/575652.jpg'
},
summary: '<p>The winner of 6 Emmy Awards including Outstanding Drama Series, <b>Homeland</b> is an edge-of-your-seat sensation. Marine Sergeant Nicholas Brody is both a decorated hero and a serious threat. CIA officer Carrie Mathison is tops in her field despite being bipolar. The delicate dance these two complex characters perform, built on lies, suspicion, and desire, is at the heart of this gripping, emotional thriller in which nothing short of the fate of our nation is at stake.</p>',
}
This is the second object below. Notice how there's no comma separating both objects
{
id: 1,
url: 'https://www.tvmaze.com/shows/1/under-the-dome',
name: 'Under the Dome',
language: 'English',
genres: [ 'Drama', 'Science-Fiction', 'Thriller' ],
status: 'Ended',
runtime: 60,
averageRuntime: 60,
premiered: '2013-06-24',
schedule: { time: '22:00', days: [ 'Thursday' ] },
rating: { average: 6.6 },
image: {
medium: 'https://static.tvmaze.com/uploads/images/medium_portrait/81/202627.jpg',
original: 'https://static.tvmaze.com/uploads/images/original_untouched/81/202627.jpg'
},
summary: "<p><b>Under the Dome</b> is the story of a small town that is suddenly and inexplicably sealed off from the rest of the world by an enormous transparent dome. The town's inhabitants must deal with surviving the post-apocalyptic conditions while searching for answers about the dome, where it came from and if and when it will go away.</p>",
}
My question now is how do I convert both objects to an array and send as a response from my own API. API code is below:
module.exports = {
fetchAll: async (req, res, next) => {
var idsArr = [];
var showsArr;
var shows;
try {
let wishlist = await Wishlist.find({});
if (wishlist == null) {
res.status(404)
.json({
success: false,
msg: 'No Movies Found in Wishlist',
wishlist: []
})
}
// console.log(wishlist);
wishlist.map((item) => {
idsArr.push(item.id);
})
console.log(idsArr);
idsArr.map(async (id) => {
shows = await axios.get(`https://api.tvmaze.com/shows/${id}`);
console.log(shows.data);
// console.log(showsArr);
// showsArr = [shows.data];
})
console.log(showsArr);
return res.status(200)
.json({
success: true,
msg: 'All Movies in Wishlist Fetched',
wishlist: showsArr
})
} catch (err) {
console.log(err);
next(err);
}
},
... // other methods
}
I have tried creating an empty array. shows.data which is the actual response and then I've tried adding it to my array using showsArr.push(shows.data) previously without much success. I get undefined when I log to console.
Here the ids range from 1 to 240+, in case one wants to try out the endpoint - https://api.tvmaze.com/shows/${id}
How would I go about achieving this? Thanks.
Just like when converting the wishlist array to an array of ids, you would need to push the data items into your new showsArr.
However, this doesn't actually work, since it's asynchronous - you also need to wait for them, using Promise.all on an array of promises. And you actually shouldn't be using push at all with map, a map call already creates an array containing the callback return values for you. So you can simplify the code to
module.exports = {
async fetchAll(req, res, next) {
try {
const wishlist = await Wishlist.find({});
if (wishlist == null) {
res.status(404)
.json({
success: false,
msg: 'No Movies Found in Wishlist',
wishlist: []
})
}
const idsArr = wishlist.map((item) => {
// ^^^^^^^^^^^^^^
return item.id;
// ^^^^^^
});
console.log(idsArr);
const promisesArr = idsArr.map(async (id) => {
const show = await axios.get(`https://api.tvmaze.com/shows/${id}`);
console.log(shows.data);
return shows.data;
// ^^^^^^^^^^^^^^^^^^
});
const showsArr = await Promise.all(promisesArr);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
console.log(showsArr);
return res.status(200)
.json({
success: true,
msg: 'All Movies in Wishlist Fetched',
wishlist: showsArr
})
} catch (err) {
console.log(err);
next(err);
}
}
};

Mongoose/Typescript issue

So, i have to update some data inside my main interface, the problem is that, when i tried to do that, it complains because .save() is not defined
So i create another interface to that data in order to extends Document so i can have access to .save()
But, here's the new error....
const theComment: IComment
Type 'Comment' is missing the following properties from type 'IComment': $getAllSubdocs, $ignore, $isDefault, $isDeleted, and 47 more.
Here's my code
What i want to update ( the problem is theComment )
export const editComment = async (req: Request, res: Response) => {
const {publicationId} = req.params;
const { identifier, body, commentId } = req.body;
// Check id's
if (!mongoose.Types.ObjectId.isValid(identifier!))
return res.status(400).json({ Message: "identifier not valid" });
if (!mongoose.Types.ObjectId.isValid(publicationId!))
return res.status(400).json({ Message: "identifier not valid" });
if (!mongoose.Types.ObjectId.isValid(commentId!))
return res.status(400).json({ Message: "identifier not valid" });
// Find pub
const thePub: Ipub = await Publication.findById(publicationId);
// Find user
const theUser: Iauth = await User.findById(identifier);
// Find comment, make sure that comment is from that user
const theComment: IComment = thePub.comments!.find((f) => f.id === commentId && f.identifier === theUser.id)!;
if(!theComment) return res
.status(405)
.json({ Message: "You are not the owner of the comment || Comment doesn't exist" })
// Make sure body is not empty
if(!body) return res.status(404).json({Message: 'No data provided'})
try {
// Update comment and perfil if it has changed
theComment.body = body;
theComment.perfil = theUser.perfil;
await theComment.save()
return res.json(theComment)
} catch (err) {
console.log(err);
return res.status(500).json({ Error: "the API failed" });
}
};
Main interface
export interface Ipub extends Document {
id?: string;
body: string;
photo: string;
creator: {
name: string;
perfil?: string;
identifier: string;
};
likes?: Likes[];
comments?: Comment[];
createdAt: string;
}
Data's interface that i want to update inside my main interface
export interface IComment extends Document {
id?: string;
body: string;
name: string;
perfil?: string;
identifier: string;
createdAt: string;
likesComments?: Likes[];
}
What can i do ? how can i solve it ?
Thanks for your time comunnity !!
TS Compiler says the object described by Comment interface doesn't have .save() method. And as far as I presume it should not have because it's not a MongoDB document.
The time you inherit all props from Document interface the compiler throws the error saying that types Comment & IComment are not compatible because the second one has Document props, and the first one doesn't. To fix it you should just cast the type directly like this:
const theComment = thePub.comments!.find((f) => f.id === commentId && f.identifier === theUser.id)! as IComment;
But in order to update the comment you have to update 'whole' Publication document(for example, by using aggregate):
Publication.update(
{
"id": publicationId,
"comments.id": commentId,
"comments.identifier": theUser.id,
},
{ $inc: {
"comments.$.body": body,
"comments.$.perfil": theUser.perfil,
}},
false,
true,
);
Or the best option I think is to use relationships between Documents. Create another Document named Comment and save all related comments there. In that case you will have an ability to use .save() and other methods provided.

componentDidUpdate - What am i doing wrong?

I am trying to bring the error messages from the backend to the frontend using the MERN stack. I am able to receive the error messages in the developer tools, but not able to show the messages in the UI. I am using Redux for the state manager. The console.log() in my concomponentDidUpdate is not working when I am submitting the form. Can someone please clarify what is wrong with my code?
state = {
name: '',
email: '',
phoneNo: null,
dateOfBirth: new Date,
gender: 'Select',
education: '',
checkBox: false,
msg: null
}
static propTypes = {
addInfo: PropTypes.func.isRequired,
info: PropTypes.object.isRequired,
error: PropTypes.object.isRequired
}
componentDidUpdate(prevProps, nextProps) {
const { error } = this.props;
if (error !== nextProps.error) {
console.log("Working");
if (error.id === 'REGISTER_FAIL')
this.setState({ msg: error.msg.msg });
else
this.setState({ msg: null });
}
}
componentDidUpdate() will not be invoked in initial mount and will only be called during component rerenders.
If you need it to be called in initial mount as well as subsequent updates, either duplicate it to componentDidMount or use static getDerivedStateFromProps
Following this docs, componentDidUpdate receive 3 params: prevProps, prevState and snapshot.
In your code, you used it like the next props:
componentDidUpdate(prevProps, nextProps)
It should be something like:
componentDidUpdate(prevProps, prevState){
const { error } = this.props;
// React will call didUpdate after component is updated
// this.props is the nextProps.error as you trying to do
if (error !== prevProps.error) {
console.log("Working");
if (error.id === 'REGISTER_FAIL')
this.setState({ msg: error.msg.msg });
else
this.setState({ msg: null });
}
}
Or you will want to use UNSAFE_componentWillReceiveProps
UNSAFE_componentWillReceiveProps(nextProps){}

how can we use data received through axios put request on client side in mern stack?

I have sent category Id to the Nodejs through this code
const catHandler = async (catId) => {
const data = Axios.put('/api/products/categories/filter', {catId: catId},
{
"headers": { "content-type": "application/json", },
}
).then( categoriesProducts => {
console.log(categoriesProducts.data.products)
})
}
and this is my route for this
router.put('/categories/filter', async (req, res) => {
try {
const findCategory = await Category.find({ _id: req.body.catId });
if (findCategory) {
const productsByCategory = await Product.find(
{ category: req.body.catId }
).then(products => {
res.status(200).json({ products });
})
}
} catch (error) {
console.log('categories filter error', error)
}
})
The products of specific category are being shown in the console.log(categoriesProducts.data.products) on the react front end side like below
0: {_id: "5f7c88756746363148792982", name: "Simple Pizza", price: 5.6, image: "1601996916374.jpg", countInStock: 434, …}
1: {_id: "5f7c88976746363148792983", name: "Smoked Pizza", price: 7.8, image: "1601996951114.jpg", countInStock: 88, …}
2: {_id: "5f7c88c56746363148792984", name: "Large Cheezy Pizza", price: 9.4, image: "1601996997474.jpg", countInStock: 434, …}
But I want to display these products on the front end side. I have tried to use axios.get method but with get method how can I can send category Id to backend. So if any one has any idea how to do that Plz guide me.
you can use the query params with get method in node js
you can get query params by req.query in nodeJs
example
passing category id from front end -
api/products/categories/filter?cid=1
getting query param in the backend
const catId = req.query.cid
You can use below code to pass parameter to API get method.
fetch("/api/products/categories/filter?catId" + catId)
.then((res) => res.json())
.then((json) => {
this.setState({
Items: json,
});
});
And also you first create new state named as Items such as below.
this.state = {
Items: []
};
And finally Iterate on Items.
BR

Node js MongoDb specific page view counter

I'm making app with MEAN stack and I want on every get request to increase viewCounter on specific document ( Property ) inside collection.
If i put this code inside get request of requested property
Property.findByIdAndUpdate('id', { $inc: { counter: 1 } }, {new: true})
It will increase loading of data and i want to do that after user gets his data.
So is the best way to do this just to send additional request to the database after initial data is loaded ?
Property {
name: '',
description: '',
...,
viewCounter: 5
}
exports.getProperty = catchAsync(async (req, res, next) => {
query = await Property.findById(req.params.id).lean();
if(!query) {
return next(new AppError('No property found with that ID', 404))
}
res.status(200).json({
status: 'success',
data: {
query
}
})
})
Node events can be used to keep the counter of events.
Official document
Reference for code
eventEmitter.on('db_view', ({ parameters }) => {
eventTracker.track(
'db_view',
parameters
);
})
eventEmitter.on('db_view', async ({ user, company }) => {
Property.findByIdAndUpdate('id', { $inc: { counter: 1 } }, {new: true})
})
Try to send request after making sure your document has loaded.
angular.element($window).bind('load', function() {
//put your code
});

Resources