Express - return certain documents with named route parameters using axios - mongodb

I'm having trouble communicating between the frontend and backend for a selected GET request.
I am using a React frontend with an express/mongoose setup out in the backend.
In the frontend, I do a GET call using axios for:
axios.get('/api/orders/', {
params : {
name: this.props.user.name // user name can be Bob
}
})
And in the backend I'm having a hard time understanding the correct method I would need to do to query the database (example below doesn't work). I found stuff with .select but even then I still can't get it to work:
router.get('/orders', function(req, res) {
Order.find({}).select(req.params).then(function (order) {
res.send(req.params);
})
});
I also tried doing this to see if I can even get the params to send properly and to no demise:
router.get('/orders/:name', function(req, res) {
res.send('client sent :',req.query.name);
});
The orders document model holds objects that house an ordered array and a name (type: String) attached to the object. The Mongoose scheme for the order:
const orderScheme = new Schema({
name : { type : String },
orders : { type : Array}
});
In my MongoDB, I can see all the "Master Orders" send back. Each master order has the name of who submitted it, plus all the orders within (there can be a ton of orders).
What I'm trying to exactly do is pull up all orders that have a certain name. So if I search "TestAccount", I'll get all of bob's orders. I've included an image below:
Any pointers?

Client-side:
axios.get('/api/orders/' + this.props.user.name)
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
You need to handle the Promise when resolved/rejected.
Server-side:
router.get('/orders/:name', function(req, res) {
return Order.find({name: req.params.name}).then(function(orders) {
// return orders when resolved
res.send(orders);
})
.catch(function (error) {
// handle error
console.log(error);
})
});
You did not specify a named route parameter in your route path.
You also aren't accessing the name property by using req.params only.
You should use Model.find() conditions parameter to specify which document[s] you're trying to find. Query.prototype.select() is for filtering document fields.

Related

Mongoose: Defining 404 status for not finding a document doesnt work

I,m learning MongoDB and mongoose and now I have a problem in defining a 404 status for my route handler. Here is the code:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id
try {
const user = await User.findById(_id)
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
Now if I give it an id that doesn't exist, it doesn't give me 404 Not Found status. it only executes the catch block which is not what I want.
I would appreciate it if you tell me where I made mistake or tell me a way to get error handling for that.
Thanks
The problem
As you can see in the log
CastError: Cast to ObjectId failed for value "6082d50a2c89db3164" at path "_id" for model "User"
It means : the value you provide to findById function ("6082d50a2c89db3164") is not a valid ObjectId.Then the catch block is executed.
Suggestion
1. Validate the parameter before query in database
I understand that you're trying to provide some id that doesn't exist in the database to test. But IMHO, there a difference between 2 cases :
you provide a valid id, and this id cannot be found in the database. It should return 404 in this case
you provide an invalid id in the request, it could be a string like "6082d50a2c89db3164", or even "#Q*&$(##*" or anything we could imagine. For this case, it could be better if we validate the input (req.params._id) to ensure that the format is valid. The code will be something like this:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id;
// validate params
if(!isValidateObjectId(_id)) { // the function we need to write
res.status(200).send("Invalid params"); // you can define your status and message
return;
}
// good params, get user from database
try {
const user = await User.findById(_id)
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
2. Use findOne() method instead of findById
If you want a simpler solution, don't use findById because the function expects a valid ObjectId. We can use findOne() method :
app.get('/users/:id', async (req, res) => {
const _id = req.params.id
try {
const user = await User.findOne({_id : _id})
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
(IMHO, the first solution is better though..)
Some helpful link :
https://docs.mongodb.com/manual/reference/method/ObjectId/
Can I determine if a string is a MongoDB ObjectID?
https://mongoosejs.com/docs/api.html#model_Model.findOne

I want my Dialogflow bot to say a message which includes the result from a MongoDB query

I am querying a collection in MongoDB from Dialoglow Fulfillment. I then want my bot to respond with a message which includes this query. The code in the function of the Dialogflow Fulfillment is:
function readRecord(agent){
var name;
MongoClient.connect(uri, function(err, client) {
const collection = client.db("test").collection("data");
collection.find({fname: 'Example'}).toArray(function(err, result){
if (err) throw err;
console.log(result);
name = result.lname;
agent.add("Found last name: ", name);
});
client.close();
});
}
When I run this I get no response from my from the bot. When I console.log(result) the information is there but I can't seem to get the bot to say it.
The issue is that the intent handler expects you to return a Promise if you are doing any asynchronous functions - like accessing a database. The easiest way to do this is to change from using callbacks with MongoDB to using versions of the functions that return Promises, and then to return the promise.
I haven't tested, but something like this might work
return MongoClient.connect( uri )
.then( client => {
const collection = client.db("test").collection("data");
return collection.find({fname: 'Example'}).toArray();
})
.then( result => {
let name = result[0].lname;
agent.add("Found last name: "+name);
});

Why is JQuery casting a string to _id for Mongodb in this? (Please read EDIT)

I have a route that adds an image (a meme) like this:
// add new image by URL
app.post('/api/addMeme', function (req, res) {
var meme = new Meme({
title: req.body.title.trim().toLowerCase(),
image: req.body.image,
meta: {
votes: 0,
favs: 0
},
related: []
});
// Save meme to database
meme.save(function (err) {
if (err) throw err;
Meme.find({}, function (err, meme) {
if (err) throw err;
io.emit('new meme', meme);
});
res.send('Succesfully inserted meme.');
});
});
It takes the only two attribute title and image given by client side ajax and add it to my Mongodb database named Meme. Emit the updated database using socket.io. Both title and image are String type. image is suppose to be an URL to an image.
Now, I'm not ashamed to admit it, but my friend trolled my site and sent image = "www.pornhub.com" to this route and it crashed my database/site. Whenever I go and try to retrieve the image by its _id, I get the error:
CastError: Cast to ObjectId failed for value "www.pornhub.com" at path "_id" for model "meme"
EDIT: it looks like the error is actually coming from the route
app.post('/api/vote', function(req, res){
Meme.findOneAndUpdate({_id: req.body.id}, {$inc : {'meta.votes' : 1}}, {new: true}, function (err, meme) {
if (err) throw err;
if (!meme) return res.send('No meme found with that ID.');
io.emit('new vote', meme);
res.send('Succesfully voted meme.');
});
});
where a POST request is updating the database, and there's a cast error where the _id is given as a string?
The client side script that's doing this is
$("#vote").click(function(){
$.ajax({
type: "POST",
url: '/api/vote',
data: {
id: App.meme._id
},
success: function (data, status) {
console.log(data);
}
});
return false;
});
where App is a Express-state exposed data for which meme, the database, lives under.
But this error ONLY occurs on the object with image = "www.pornhub.com". My guess is that somewhere in the HTML, a cross-site href is visiting www.pornhub.com and somehow App is getting distorted? It doesn't fully make sense why id: App.meme._id would give www.pornhub.com as its value.

Get all matching items using ids array form database

I can't receive list of items that matches with my array of ids.
This is PART of code in Angular component:
this.orderService.getSpecyficOrders(ids)
.subscribe(orders => { ...
Where ids is an array of
[{_id : ID },{_id : ID },{_id : ID },]
ID is "5235sd23424asd234223sf44" kind of string form MongoDB documents.
In angular service file I have imported:
Http, Headers, and import 'rxjs/add/operator/map';
Here is code in service in Angular:
getSpecyficOrders(ids){
return this.http.get('/api/ordersspecyfic', ids)
.map(res => res.json());
}
In express file I have require: multer, express,router,mongojs, db
And here is part of code in express, call to mongodb:
router.get('/ordersspecyfic', function(req, res, next){
var ids = req.body;
ids = ids.map(function (obj){ return mongojs.ObjectId(obj._id)});
db.orders.find({_id: {$in: ids}}, function(err, orders){
if(err){
res.send(err);
}
res.json(orders);
});
});
And I'm getting error:
Uncaught Response {_body: "TypeError: ids.map is not a function
&n…/node_modules/express/lib/router/index.js:46:12)↵", status:
500, ok: false, statusText: "Internal Server Error", headers:
Headers…}
Console.log in express file
is showing me that req.body is an empty object {}
As far as I know req.body is not an array, but I don't know if this is only problem with that code.
All others request of get single element, get all items etc. are working fine.
I just can't get this one working..
I assume you are trying to send ids to your server side with
return this.http.get('/api/ordersspecyfic', ids)
but http.get api doesn't work like that
get(url: string, options?: RequestOptionsArgs) : Observable
In order to send this data to your back-end you should use the post api
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post('/api/ordersspecyfic', ids, options)
post(url: string, body: any, options?: RequestOptionsArgs) : Observable
Source:https://angular.io/docs/ts/latest/api/http/index/Http-class.html
Two errors, backend and frontend.
Frontend error
You say this.http.get('/api/ordersspecific', ids);. This does nothing - or specifically, this only tries to get /api/ordersspecific. It doesn't send ids, your second parameter doesn't match any RequestOptions. In other words, your ids are ignored.
You'd want to append this as a query string. Check here how to add querystring parameters. But in short, it'd be something simple like:
return this.http.get('/api/ordersspecyfic?ids=<id1>&ids=<id2>...'
Backend error
You're reading stuff from body. It's a GET request, there should be no body. Read this from querystring:
router.get('/ordersspecyfic', function(req, res, next){
var ids = req.query.ids;
});

Express JS routing based on the user names

I am working on a express js project.I have got all my basic routing set up working perfectly. Usually when I want to search a record based on id I do this:
router.route('/sensors_home/:sensor_id')
.get(function (req, res) {
Sensor.findById(req.params.sensor_id,function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});
This allows me to retrieve the data when I do http://localhost:4000/sesnors_home/45000cbsfdhjbnabfbajhdb
(45000cbsfdhjbnabfbajhdb = Object id from the MongoDB )
Now my goal is to have several users to my application. I have my mongoose schema set up and the mongoDB looks like this :
Here is the issue: I wanna retrieve data corresponding to John Peterson based on his _id that is "John".Instead of doing this http://localhost:4000/sesnors_home/45000cbsfdhjbnabfbajhdb I wanna do something like this http://localhost:4000/sesnors_home/John and retrieve all the data specific to John. I tried various methods but still stuck with this issue. I tried using req.params._id and also some Mongodb queries on the User Collection but still no luck. Please suggest some ideas.
Thanks!
UPDATE:
I tried using the following code :
router.route('/sensors_home/:id')
.get(function (req, res) {
res.send(_id.toString());
User.findOne({_id: req.params._id} ,function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});
This gives me the following error :
ReferenceError: _id is not defined
Have you tried the following?
router.route('/sensors_home/:_id')
.get(function (req, res) {
Sensor.findOne({_id: req.params._id},function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});