I've been trying to figure out why my HTTP Put has not been working after I use it once. When I click a button, I push the current user's id into an array like so:
$scope.currentUser = {
'eventsAttending' = [];
}
$scope.attending = function(event, id){
if($cookieStore.get('token')){
$scope.currentUser = Auth.getCurrentUser();
}
$scope.currentUser.eventsAttending.push(event._id);
$http.put('/api/users/' + $scope.currentUser._id, $scope.currentUser)
.success(function(data){
console.log("Success. User " + $scope.currentUser.name);
});
}
And my HTTP Put function is like so:
var express = require('express');
var controller = require('./user.controller');
var config = require('../../config/environment');
var auth = require('../../auth/auth.service');
router.get('/:id', controller.getEvents);
var router = express.Router();
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
User.findById(req.params.id, function (err, user) {
if (err) { return res.send(500, err); }
if(!user) { return res.send(404); }
var updated = _.merge(user, req.body);
updated.markModified('eventsAttending');
updated.save(function (err) {
if (err) { return res.send(500, err); }
return res.json(200, user);
});
});
};
In my HTML page I have multiple events I can attend, and each event has the button called Attend where I call $scope.attending and the function is called and the HTTP Put occurs. All of this works for the first event I choose to attend. However, when I click the Attend button for another event, I get an error that says:
{"message":"No matching document found.","name":"VersionError"}
And I have no idea why. The error occurs when I try to do updated.save() in the mongoose call and I get res.send(500, err)
I tried to look at http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html to solve the issue as I did some googling but I keep getting an error that says:
Undefined type at `versionKey`
Did you try nesting Schemas? You can only nest using refs or arrays.
Upon adding into my schema:
var UserSchema = new Schema({
versionKey: 'myVersionKey',
...
I also tried to change the .save() function into .update() as someone suggested it online but that seemed to give me even more errors. Any ideas how to fix this? That would be much appreciated!
I think the issue you may be experiencing (as was the case when I was getting this error from a similar action) is that after the first update, the '__v' VersionKey property on the newly updated document has changed, but you might not be updating that property on the object you have in the browser. So when you go to update it again, you're sending the old '__v' VersionKey, (even though you updated the 'eventsAttending' property) and that document conflicts the newer VersionKey. This would assume that the Auth.getCurrentUser(); function returns the whole document object from mongo.
What I did to fix this was simply add delete entity.__v; to delete the old VersionKey from the document before sending it with the request. Better yet, I'd recommend updating the properties your API sends back when returning documents so this issue doesn't happen in the first place.
Related
The Nodejs functions return an error from try/catch scope, such as the one below if the user doesn't exist of if a database is not reachable:
router.delete('/delete/:email', async (req, res) => {
var email = req.params.email;
try {
let result = await User.remove({"email": email});
res.status(204).send(email);
} catch (err) {
res.status(400).send(err);
}
});
I can also return the Error from Nodejs server by myself:
return res.status(400).send(new Error(`The user with email ${email} doesn't exist.`));
The first problem is that I can't find the error message that is embedded somewhere deep in the body the returned Error object. It is stored in one of its 100+ attributes. Where should I look for it so I could display in on a screen for the end user to read it?
Then, the err object generated by the try/catch scope has a set of different attributes comparing to the Error object created with new Error("Here is my error message"). Is there a way to normalize the returned Errors so they all have the same or similar attributes?
You don't need to return the whole error object from the server, and arguably shouldn't since error messages can expose internals about your code and infrastructure.
One way you could handle this is to format and return an error message from the server yourself. Assuming you're using express this would look something like:
return res.status(400).json({ message: `The user with email ${email} doesn't exist.` });
Alternatively you could use an error handling middleware like strong-error-handler found here: https://github.com/strongloop/strong-error-handler which automatically builds a json formatted message that's easier to parse, but keep in mind that the content of the message differs depending on whether you set debug mode to true or no.
If you want to develop a secure web application with nice error handling, i will suggest you the following structure.
Step 1. At front end divide your api calls in four main operations for e.g. inset,update,query and filter.
now whenever your page loads and you want to show some data fetched from server then your api call must be like 'https://domainname.tld/server/query' and send some payload with this api call according to need of your data requirements to be fetched.
At backend probably at Server.js handle like this :
app.all("/server/query", function (req, res) {
try {
console.log(a);
// some database or io blocking process
} catch (error) {
// error handling
var err = writeCustomError(error.message || error.errmsg || error.stack);
res.status(417).json(err).end();
}
});
function writeCustomError(message) {
var errorObject = {};
errorObject.message = message;
errorObject.code = 10001; // as you want
errorObject.status = "failed";
return errorObject;
}
in try block you can also handle logical errors using same function i.e writeCustomError
So if you use this approach you can also implement end-to-end encryption and send only eP('encrypted payload') and eK('encryption Key'),by doing this end users and bad end users even can not evaluate your serve API calls.
If you are thinking how will you route different paths at server then simplest solution is send uri in payload from client to server for e.g
User wants to reset password :-
then
call api like this
https://domain.tld/server/execute and send Json object in payload like this {uri:"reset-password",old:"",new:""}.
at backend
use
app.all("/server/execute", function (req, res) {
try {
// decrypt payload
req.url = payload.uri;
next();
} catch (error) {
// error handling
var err = writeCustomError(error.message || error.errmsg || error.stack);
res.status(417).json(err).end();
}
});
app.all("/reset-password", function (req, res) {
try {
// reset logic
} catch (error) {
// error handling
var err = writeCustomError(error.message || error.errmsg || error.stack);
res.status(417).json(err).end();
}
});
so in this way only developer know where password reset logic and how it can called and what parameters are required.
I will also suggest you to create different router files for express like QueryRouter,InsertRouter etc.
Also try to implement end-to-end encryption.Any query regarding post,kindly comment it.
Hi fellow developers!
I've got this error showing up in my console when I try to save two identical documents in a collection in MongoDB that has nothing to do with the index shown in the error.
Here's the error: E11000 duplicate key error collection: Bohemian.orders index: user.email_1 dup key: { user.email: null }
Now this makes no sense, because I'm trying to save an Order document in a separate collection, which has nothing to do with the user router I had set up previously.
Here is the schema and model code:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
amountToPay: Number,
});
const Order = mongoose.model("Order", orderSchema);
module.exports.Order = Order;
As shown here, I am only trying to save the amount to be payed into the database in a separate collection.
Here is the router file:
const express = require('express');
const router = express.Router();
const { Order } = require('../models/Order');
router.get("/", async (req, res, next) => {
try {
const orders = await Order.find();
if (orders.length === 0) return res.status(404).send("There are currently no orders");
res.send(orders);
} catch (ex) {
console.error(ex);
next();
}
});
router.post("/", async (req, res, next) => {
try {
const order = new Order({
amountToPay: req.body.amountToPay
});
await order.save();
res.send(order);
} catch (ex) {
console.error(ex);
next();
}
});
module.exports = router;
As you can see there is nothing relative to the error that I'm getting and I have no clue why I'm getting a duplicate user.email = null key , when I haven't made any reference to the User model or router.
Here is the POST call I'm making from POSTMAN to test:
Pretty straight forward, nothing extreme, nothing tangled, right? Well the first ever POST call saves the document in the Database, but from then on I keep getting the same error. The only thing I can take from that is that when I save the first document, Mongo looks for the user.email property when I'm creating the new instance of Order and when it doesnt find it, it creates it with a value of null and then the next document would naturally be a duplicate, hence the error. But I'm confused, because this model and router should not absolutely nothing to do with the user ones.
Here is the error:
So please if anyone can help me understand why MongoDB is screwing with me or where I'm making a mistake, I would really appreciate it.
I found out what the problems was, thanks to Molda:
At some point I had created these indexes, which I'm still unsure when and how, but I did. Which essentially lead to this error, when I tried to save the second document.
A simple quick console log of the indexes in the collection showed that I had that index in there.
const indexes = await Order.collection.getIndexes();
console.log(indexes);
Then I removed them using this method:
await Order.collection.dropIndexes("user.email_1");
And everything worked flawlessly from there.
I hope this helps anyone in this situation in the future and thanks Molda! :)
I have a Node.js server which queries MySQL database. It serves as an api end point where it returns JSON and also backend server for my Express application where it returns the retrieved list as an object to the view.
I am looking into implementing flat-cache for increasing the response time. Below is the code snippet.
const flatCache = require('flat-cache');
var cache = flatCache.load('productsCache');
//get all products for the given customer id
router.get('/all/:customer_id', flatCacheMiddleware, function(req, res){
var customerId = req.params.customer_id;
//implemented custom handler for querying
queryHandler.queryRecordsWithParam('select * from products where idCustomers = ? order by CreatedDateTime DESC', customerId, function(err, rows){
if(err) {
res.status(500).send(err.message);
return;
}
res.status(200).send(rows);
});
});
//caching middleware
function flatCacheMiddleware(req, res, next) {
var key = '__express__' + req.originalUrl || req.url;
var cacheContent = cache.getKey(key);
if(cacheContent){
res.send(cacheContent);
} else{
res.sendResponse = res.send;
res.send = (body) => {
cache.setKey(key,body);
cache.save();
res.sendResponse(body)
}
next();
}
}
I ran the node.js server locally and the caching has indeed greatly reduced the response time.
However there are two issues I am facing that I need your help with.
Before putting that flatCacheMiddleware middleware, I received the response in JSON, now when I test, it sends me an HTML. I am not too well versed with JS strict mode (planning to learn it soon), but I am sure the answer lies in the flatCacheMiddleware function.
So what do I modify in the flatCacheMiddleware function so it would send me JSON?
I manually added a new row to the products table for that customer and when I called the end point, it still showed me the old rows. So at what point do I clear the cache?
In a web app it would ideally be when the user logs out, but if I am using this as an api endpoint (or even on webapp there is no guarantee that the user will log out the traditional way), how do I determine if new records have been added and the cache needs to be cleared.
Appreciate the help. If there are any other node.js caching related suggestions you all can give, it would be truly helpful.
I found a solution to the issue by parsing the content to JSON format.
Change line:
res.send(cacheContent);
To:
res.send(JSON.parse(cacheContent));
I created cache 'brute force' invalidation method. Calling clear method will clear both cache file and data stored in memory. You have to call it after db change. You can also try delete specified key using cache.removeKey('key');.
function clear(req, res, next) {
try {
cache.destroy()
} catch (err) {
logger.error(`cache invalidation error ${JSON.stringify(err)}`);
res.status(500).json({
'message' : 'cache invalidation error',
'error' : JSON.stringify(err)
});
} finally {
res.status(200).json({'message' : 'cache invalidated'})
}
}
Notice, that calling the cache.save() function will remove other cached API function. Change it into cache.save(true) will 'prevent the removal of non visited keys' (like mentioned in comment in the flat-cache documentation.
so my question is very specific. Whenever I run this bit from my page I get an error if I don't input the CORRECT ID I need to search for in the API. It doesn't know what to do when it doesn't make a valid API call because the query string is incorrect. How do I go about redirecting to a different page WHEN there's an error like that or how do I prevent it from STOPPING the program? I'm assuming there's a try catch in here but I tried it multiple different ways and I'm still confused because it doesn't work. Help please! I'm new to this... Here's the snippet. The request portion of the code is where the error occurs if the "bnetID" is not a valid ID. If it is valid it runs perfectly fine...
// Make a GET request to the /results page (When submit is pressed)
app.get("/results", function(req, res){
// Retrieve bnetID and REGION from the FORM
var bnetID = req.query.bnetID;
var region = req.query.region;
// Replace the # with a -
bnetID = bnetID.replace("#", "-");
// Create the query string
var url = "http://ow-api.herokuapp.com/profile/pc/"+ region +"/"+bnetID;
// Make the API request
request(url, function(err, response, body){
if(err){
console.log(err);
} else {
var playerData = JSON.parse(body);
playerData = findImportantData(bnetID, playerData);
checkIfExists(bnetID, playerData);
res.render("results", {data: playerData});
}
})
});
Why don't you handle what you want to do if there is an error?
if(err){
console.log(err); // change this to whatever you want to do
}
I'm having issues updating a document from within a find using Mongoose. The issue is only when I attempt to overwrite the document with an object (e.g doc = req.body). I am however able to directly set properties of the original document to a specific string (e.g. doc.name = 'jason borne';).
I've verified that res.body is an object, so I don't see why I'm unable to set it this way.
Client.findById(req.params.client_id, function (err, client) {
if (err)
return next(new restify.InternalError(err));
// client.name = 'jason borne';
client = req.body;
client.save(function(err) {
if (err)
return next(new restify.InternalError(err));
res.send(client);
});
});
When attempting to set the doc to an object, I receive the error:
TypeError: Object # has no method 'save'
I'm aware that I can do an update with a simple Client.update(...) command, however this method does not allow my schema middleware or validation to run (which is notated in the Mongoose documentation).
Any thoughts? I'm new to Node, and Mongoose.
You need to use something like underscore's extend method to copy the properties of req.body into the client object instead of just re-pointing client to req.body as you are now.
var _ = require('underscore');
Client.findById(req.params.client_id, function (err, client) {
if (err)
return next(new restify.InternalError(err));
_.extend(client, req.body);
client.save(function(err) {
if (err)
return next(new restify.InternalError(err));
res.send(client);
});
});
The symptoms you're now getting are caused by the fact that you're replacing a mogoose model object (with methods like save, find, etc), by a simple json object parsed from your body, which is missing save.
Try doing an update instead of find/save.
Client.update({_id: req.params.client_id}, {$set : req.body}, function(err) {...});
Or try to merge your req.body to the client object.