PUT request only occurs on second attempt - express

I have an express route that changes a Post's "checked" value to it's opposite boolean value (true => false, false => true). For some reason, the API return the opposite of what is in the DB. If I do a GET request after a PUT request, I get the opposite boolean value of what the PUT route gives me, which is accurate to the state of the DB.
Here is the route. Anyone see what's going on here?
app.put('/posts/:id', jsonParser, (req, res) => {
let id = req.params.id;
Post.findById( id )
.then(post => {
if(!post) {
return res.status(404).json({ message: 'Post not found' });
}
return Post.findByIdAndUpdate(id, { "checked": !post.checked })
})
.then(result => {
return res.status(202).json(result.apiRepr())
})
.catch(err => {
if(err) {
return res.status(500).json({ message: "There was a problem"})
}
});
});
It appears the .findByIdAndUpdate part IS updating the DB, but then the object below that gets returned is still the old data.

By default findByIdAndUpdate return the original document, you have to set the new option to true in order to get the modified document rather than the original:
return Post.findByIdAndUpdate(id, { "checked": !post.checked }, { new: true }) ...
findByIdAndUpdate Api doc

Related

Callback function 'next' is executed automatically without making a call to it

I have this POST method which uses FetchURL middleware to fetch data from the url submitted by the user.
router.post('/', FetchURL, (req, res) => {
console.info('data received');
...
})
Everything works with response.ok being true, but the contrary case doesn't quite work as expected.
I don't want next to be called when response.ok equals false.
But I get to see "data received" logged to the console which means the next function does get called on its own.
fetch_url.js
function FetchURL(req, res, next) {
fetch(req.body.input_url)
.then(response => {
if(response.ok)
return response.json();
// else render error message on the client machine
res.status(response.status)
.render('index', {
errStatus: [response.status, response.statusText]
});
/* Throwing an Error here is the only way I could prevent the next callback */
// throw new Error(`Request failed with status code ${response.status}.`);
})
.then(data => {
req.data = data;
next();
})
.catch(err => console.error(err));
}
I could not find anything relevant on the documentation of expressjs middleware. The only way I could prevent next from being called is by throwing an Error on the server.
What happens behind the scene here?
try making a second check before next is called like following
function FetchURL(req, res, next) {
fetch(req.body.input_url)
.then(response => {
if(response.ok) // wrap your response in a temporary object.
return { fail: false, data: response.json() } ;
// else render error message on the client machine
res.status(response.status)
.render('index', {
errStatus: [response.status, response.statusText]
});
/* Instead of throwing an Error, return something indicating error */
return { fail: true };
})
.then(data => {
// check if previous procedure has failed.
if(!data.fail) {
req.data = data.data;
next();
}
})
.catch(err => console.error(err));
}

Vee validate message in an extend async rule fails to show error message

Am using vee validate with vuetify and i need to check if email is unique.
So in my vuejs i have added
mounted(){
extend('unique-email', (value) => {
return this.$axios.post('/api/auth/unique-email', { email: value })
.then((res) => {
return {
valid: true,
};
}, (err) => {
return {
valid: false,
data: { message: 'Email already registered' }
};
})
}, {
immediate: false
})
}
In my vuetify i have added
<v-textfield v-model="form.email" rules="required|email|unique-emai">
The above works for all the rules but doesnt resolve the message email already registred from the unique-email rule. What do i need to add so that if the async validation fails the message from the err part is displayed.
Currently it only shows email is not valid message when the unique-email validator fails.
What am i missing?
If you look at the docs, it seems that you need to manually handle errors produced by your POST call, so instead of just returning an object in your error handler, you would do this:
extend('unique-email', (value) => {
return this.$axios.post('/api/auth/unique-email', { email: value })
.then((res) => {
return {
valid: true,
};
}, (err) => {
this.$refs.myValidationObserver.setErrors({
email: ['Email already registered']
});
})
}, {
immediate: false
})
This requires two changes beyond the extend:
add the attribute vid="message" to your ValidationProvider (VP) around your v-textfield
add the attribute ref="myValidationObserver" to your ValidationObserver that wraps the VP in point #1.
Or, maybe I'm missing something! Where did you get the idea to return the object that you had in the question? I can't see anything like that in the current docs...
return {
valid: false,
data: { message: 'Email already registered' }
};
It seems you forget to collect error information,such like:
extend('unique-email', (value) => {
return this.$axios.post('/api/auth/unique-email', { email: value })
.then((res) => {
return {
valid: true,
};
}, (err) => {
return {
valid: false,
data: { message: 'Email already registered' }
};
})
},
getMessage: (field, params, data) => data.message
)
btw, "immediate: false" is default, you don't need to explicitly define it.

Express Returning After next()

came from Java. Was messing around with ExpressJS, I return out of a function after sending a next() if I dont add the return, then code after the next() function still executes when next() is invoked, currently it works, return escapes this behaviour, but I was wondering if this is correct way to do this, or am I developing bad habbits. Nothing is really after the next() in terms of code sequence.
function('/login', (req,res, next) => {
User.findOne({
email: username
}, (err, user) => {
if (user) {
var validPassword = user.comparePassword(password);
if (validPassword) {
let token = Helpers.getJwt(user);
res.send({
success: true,
message: 'Successful Login',
token: token
})
} else {
next(
Boom.badRequest('Invalid Credentials.', {
success: false,
message: 'Credentials did not match our records.'
}));
return;
}
} else {
next(
Boom.badRequest('User not found.', {
success: false,
message: 'User was not found, please register.'
}));
return;
}
});
EDIT: I have middleware called with the next(), my error middleware.
First, You aren't next-ing to anything here(pun intended).
You call middleware:next() function if there is another middleware:function within the chain of a particular route or a group of routes app.use(middleware:function).
Example:
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id)
next()
}, function (req, res, next) {
res.send('User Info')
})
I'd advice you read on Express Middlewares.
so yeah, you want to check if the user is valid before proceeding with the request.
Below code is just to explain how middle-ware works:
The idea is that, one middleware:function process the request and passes control down to the next function within the chain.
I'm assuming your Boom.badRequest just generates a json payload e.g {};
So this is probably an elegant way to achieve what you want, but im not sure doing this for login is ideal, maybe better for checking if a user's token is valid.
app.post('/login', /*MiddleWare Function*/(req, res, next) => {
User.findOne({email: username}, (err, user) => {
if (user) {
const validPassword = user.comparePassword(req.body.password);
if (!validPassword) return res.status(401).send(Boom.badRequest('User not found.', {
success: false,
message: 'User was not found, please register.'
}));
req.token = Helpers.getJwt(user);
next();// it will move execution to the next function we have below
} else {
res.status(401).send(Boom.badRequest('User not found.', {
success: false,
message: 'User was not found, please register.'
}));
//no need to next here.
}
})
}, /*OUR NEXT FUNCTION*/(req, res) => {
res.send({
success: true,
message: 'Successful Login',
token: req.token//notice that our token can be retrieved here
})
});
So in general, you might just want to have a middleware:function that is called first on a group of specific routes.
app.use(['/users*', '/logout'], (req, res, next) => {
/*
* What this means is that for every request that maps to /users
* or /logout this middleware:function will be called first.
*/
//we can check|test if this visitor has valid credentials.
next();//passes control to app.get('/users/whatEverItIS?') or app.post('/logout');
});
app.post('/users/whatEverItIs', (req, res)=>{
res.send("I passed the middleware test that is why im called");
});
That return statement is unnecessary. Firstly due to the fact that you are not returning anything and secondly because you are passing control to your next middleware using next method, thus that return is useless there and will be returning undefined.

Post authorization doesn't work

I'm trying to restrict ability for one user to edit someone else's posts.
When I click 'Edit post' it change the author of the post, is it because of 'save' method or ?
Here is my attempt:
router.put('/posts/:id', passport.authenticate('jwt'), (req, res) => {
Post.findOne({_id: req.params.id}, (err, post) => {
if (err) throw err;
if (post.author = req.user._id) {
post.title = req.body.title,
post.content = req.body.content,
post.postImageUrl = req.body.postImageUrl
post.save((err, updatedPost) => {
if (err) throw err;
else {
res.json({message:'You have successfully updated your post', success: true});
}
});
} else {
res.json({success: false, message: 'You are not allowed to do this.'});
}
});
});
I checked post.author and req.user._id, they match.
Please check your condition for checking if the current user is the post's author.Instead of checking for equality you are trying to assign and change the author value.
if (post.author = req.user._id)
Solution:
Mongoose offers a .equals method for checking equality for object ids.Here is the link for the docs for mongodb-native driver .equals method, which is used by Mongoose.
if (post.author.equals(req.user._id))

Return list from Graphql resolve

fetchFriends: {
type: new GraphQLList(UserType),
args: {
currentId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve: (_, {currentId}) => {
return Promise.resolve()
.then(() => {
User.findById(currentId, (err, users) => {
users.getFriends((err, user) => {
console.log(user);
return user;
});
});
})
}
/* another version what i tried that returns only the initial findById user
resolve: (_, {currentId}) => {
var value = User.findById(currentId, (err, user) => {
new Promise((resolve, reject) => {
user.getFriends((err, user) => {
console.log('fetch: ', user);
err ? reject(err) : resolve(user)
});
})
})
return value;
}*/
},
I have a graphql resolve that i am getting the User object within the findById callback. that specific object calls getFriends which is a part of a mongoose plugin (friends-of-friends) and the console.log within getFriends callback contains the list in the terminal so i know getFriends
is returning the proper data but i cannot figure out how to return the value into my React-Native Component. i have tried for the past 8 hours everything i can think of and cannot get the value returned out of this function.
You're close, but there's a couple of things to keep in mind when working with resolvers:
Your resolver has to return either a value that matches the type/scalar specified in your schema or a Promise that will resolve to that value.
Mongoose operations can return a promises, and you should utilize this feature of them rather than trying to wrap callbacks inside Promises as this can easily get messy
Return statements inside callbacks at least in this context) don't actually do anything. Return statements inside a then, on the other hand, determine what the promise will resolve to (or what promise to invoke next in the chain).
I would imagine your resolver needs to look something like this:
resolve (_, {currentId}) => {
// calling exec() on the query turns it into a promise
return User.findById(currentId).exec()
// the value the promise resolves to is accessible in the "then" method
.then(user => {
// should make sure user is not null here, something like:
if (!user) return Promise.reject(new Error('no user found with that id'))
// we want the value returned by another async method, getFriends, so
// wrap that call in a promise, and return the promise
return new Promise((resolve, reject) => {
user.getFriends((error, friends) => {
if (error) reject(error)
resolve(friends)
})
})
})
}