feathersjs: How do I pass un-opinionated errors back to client - javascript

Seems like error messages are wrapped in text. Say in a model validation I just want to send "exists" to the client if a record already exists.
One the server maybe I do something like:
validate: {
isEmail: true,
isUnique: function (email, done) {
console.log("checking to see if %s exists", email);
user.findOne({ where: { email: email }})
.then(function (user) {
done(new Error("exists"));
},function(err) {
console.error(err);
done(new Error('ERROR: see server log for details'));
}
);
}
}
On the client maybe I do:
feathers.service('users').create({
email: email,
password: password
})
.then(function() {
console.log("created");
})
.catch(function(error){
console.error('Error Creating User!');
console.log(error);
});
The error printed to console is:
"Error: Validation error: exists"
How to I just send the word "exists" without the extra text? Really I'd like to send back a custom object, but I can't seem to find any examples of doing this. The closest I've seen is this: https://docs.feathersjs.com/middleware/error-handling.html#featherserror-api
But I haven't figured out how to make something like this work in the validator.

Feathers does not change any error messages so the Validation error: prefix is probably added by Mongoose.
If you want to change the message or send an entirely new error object, as of feathers-hooks v1.6.0 you can use error hooks:
const errors = require('feathers-errors');
app.service('myservice').hooks({
error(hook) {
const { error } = hook;
if(error.message.indexOf('Validation error:') !== -1) {
hook.error = new errors.BadRequest('Something is wrong');
}
}
});
You can read more about error and application hooks here

Related

Why is there no message in mongoose error object?

I am making a user signup API, where name and email are set to be unique. When I send a JSON object which has a the same email address as an existing user, it gives an error as expected.
But there is no message in that error, which i can then use to give a more usable message.
Below is the error object that i'm getting.
{ "index": 0,
"code": 11000,
"keyPattern": {
"email": 1
},
"keyValue": {
"email": "pranatkarode#rocketmail.com"
}
}
And this is my controller module,
const User=require("../models/userModels.js");
exports.signUp=(req,res)=>{
const user=new User(req.body);
user.save((err,user)=>{
if(err){
res.send(err)
}
else{
res.json({
user
})
}
})
}
Mongoose's default error object is designed for non limited use cases. So you need to look at it and depending on what it contains add custom messages. For this specific use case of yours, you can for example do it like so:
const User = require("../models/userModels.js");
exports.signUp = async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.json({
user,
});
} catch (err) {
if (err.code === 11000 && err?.keyPattern.hasOwnProperty("email")) {
err.message = "This email is alrady used.";
} else if (err.code === 11000 && err?.keyPattern.hasOwnProperty("name")) {
err.message = "This name is alrady taken.";
}
res.send(err);
}
};
I'm using async/await syntax here to have a better looking code, yours would work as well.
The solution is quite simple, just remove the await before the await user.save and boom everything should be fine... but if problem persist then check your username and password in your .env file and make sure they are correct, also you should make sure your IP address is set to both 0.0.0.0 and your current IP in the atlas.
Before:
await user.save();
After:
user

Node JS and Angular Email Verification: Anyway to send html in a response?

To start off, I do want to clarify that I know how to use APi's created in NodeJS in Angular. The problem I have is a little tricky.
I have a function that verifies the email used in registering:
exports.confirmEmail = function (req, res) {
ConfirmToken.findOne({
token: req.params.token
}, function (err, token) {
if (err) {
return res.status(500).send({
message: "Internal Server Error " + err
})
}
// token is not found into database i.e. token may have expired
if (!token) {
return res.status(400).send({
message: 'Your verification link may have expired. Please click on resend for verify your Email.'
});
}
// if token is found then check valid user
else {
Account.findOne({
_id: token._accountId,
email: req.params.email
}, function (err, user) {
if (err) {
return res.status(500).send({
message: "Internal Server Error " + err
})
}
// User does not exist
if (!user) {
return res.status(401).send({
message: 'The account does not exist'
});
}
// user is already verified
else if (user.isVerified) {
return res.status(200).send('User has been already verified. Please Login');
}
// verify user
else {
// change isVerified to true
user.isVerified = true;
user.save(function (err) {
// error occur
if (err) {
return res.status(500).send({
message: err.message
});
}
// account successfully verified
else {
return res.status(200).send('Your account has been successfully verified');
}
});
}
});
}
})
}
This is the response I get when I register an account
Now my question is: is there a way to pass in html code or have it show in a custom Angular component instead of displaying as simple plain text on the web browser as such
Your service should send a isVerified status back to the client. You are sending only a string at the moment
return res.status(200).send('Your account has been successfully verified');
based on this status, let's call it, isVerified your angular app would render a isVerfiedComponent.ts or notVerifiedComponent.ts

How can I validate this in more elegant way?

Im trying to do login/register module in my project. This is my login function. I would like to have one function that will validate all things for me so I dont have to use so many "if" statements. I was trying to do with pure function but completely don't know how to do it. Can someone help me ?
const loginUser = async (req, res, next) => {
const { password, email } = req.body;
if (!email) {
return res.status(400).json({
message: "Error: Email cannot be blank.",
});
}
if (!password) {
return res.status(400).json({
message: "Error: Password cannot be blank.",
});
}
try {
const user = await User.findOne({ email: email });
if (!user)
return res.status(400).json({
message: "Invalid user",
});
if (!validPassword(password, user.password))
return res.status(400).json({
message: "Invalid password",
});
const { name, likedArr, _id } = user;
const token = crypto.randomBytes(32).toString("hex");
const userSession = new UserSession({ userId: _id, token });
await userSession.save();
return res.status(200).json({
message: "Valid login",
token: token,
user: {
name,
likedArr,
userId: _id,
},
});
} catch (err) {
next(err);
}
};
Abstracting my comments into an answer.
On Pure Functions:
If I understand pure functions correctly, I don't think you can have a pure function that calls an external API which may fail, since the same inputs may possibly return different results depending on the external state of the API (unless that API is guaranteed pure itself somehow). (Definition of a pure function)
On Repetition:
I genuinely think you don't have a lot of repetition here. Your code is clear and only has 4 conditionals, all for things you need to test for. You could abstract the similarities of your JSON returns into something like a template string depending on the conditions, but I think that could add clutter and opacity to your code, which isn't a good trade-off if you do it too much.
If you want an example of what I mean here:
if (!email) {
return res.status(400).json({
message: "Error: Email cannot be blank.",
});
}
if (!password) {
return res.status(400).json({
message: "Error: Password cannot be blank.",
});
}
Can become...
if (!email || !password) {
return res.status(400).json({
message: `Error: ${!email ? 'Email' : 'Password'} cannot be blank.`,
});
}

How can I solve alert reference errors in Javascript?

I got this error
"type error alert is not a function ".
So I created an alert function but got this error
reference error alert is not defined
when I try to execute its function
function alertMessage(messageObject) {
alert(messageObject);
return true;
}
app.post("/login", function(req, res) {
const username = req.body.username;
const password = req.body.password
User.findOne({
email: username
},
function(err, foundUser) {
if (foundUser) {
if (foundUser.password !== password) {
alertMessage("Password Is Incorrect");
} else {
if (foundUser) {
if (foundUser.password === password) {
res.render("compose");
}
}
};
};
});
});
From app.post, I'm guessing this is code running in Node.js via Express.js. Node.js doesn't have an alert function, that's something browsers provide. In app.post you're replying to a response from the browser. If there's a login error, you need to send a reply to the post saying there's a login error. It looks like your code is expecting to present a page in response to the post (rather than being called via ajax), so you'd render a page saying the login failed. (You might consider using ajax instead, so the page doesn't have to refresh in this case, but that's beside the point.)

Parse Server / JS SDK, error 206 when saving a user object

I am having trouble using the Parse Server JS SDK to edit and save a user.
I am signing in, logging in and retrieving the user just fine, I can call without exception user.set and add/edit any field I want, but when I try to save, even when using the masterKey, I get Error 206: Can t modify user <id>.
I also have tried to use save to direcly set the fields, same result.
A interesting thing is that in the DB, the User's Schema get updated with the new fields and types.
Here is my update function:
function login(user, callback) {
let username = user.email,
password = user.password;
Parse.User.logIn(username, password).then(
(user) => {
if(!user) {
callback('No user found');
} else {
callback(null, user);
}
},
(error) => {
callback(error.message, null);
}
);
}
function update(user, callback) {
login(user, (error, user) => {
if(error) {
callback('Can t find user');
} else {
console.log('save');
console.log('Session token: ' + user.getSessionToken());
console.log('Master key: ' + Parse.masterKey);
user.set('user', 'set');
user.save({key: 'test'}, {useMasterKey: true}).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR - ' + require('util').inspect(err));
callback(error.message);
}
);
}
});
}
And a exemple of the error:
update
save
Session token: r:c29b35a48d144f146838638f6cbed091
Master key: <my master key>
ERR- ParseError { code: 206, message: 'cannot modify user NPubttVAYv' }
How can I save correctly my edited user?
I had the exact same problem when using Parse Server with migrated data from an existing app.
The app was created before March 2015 when the new Enhanced Sessions was introduced. The app was still using legacy session tokens and the migration to the new revocable sessions system was never made. Parse Server requires revocable sessions tokens and will fail when encountering legacy session tokens.
In the app settings panel, the Require revocable sessions setting was not enabled before the migration and users sessions were not migrated to the new system when switching to Parse Server. The result when trying to edit a user was a 400 Bad Request with the message cannot modify user xxxxx (Code: 206).
To fix the issue, I followed the Session Migration Tutorial provided by Parse which explain how to upgrade from legacy session tokens to revocable sessions. Multiple methods are described depending on your needs like enableRevocableSession() to enable these sessions on a mobile app, if you're only having a web app, you can enforce that any API requests with a legacy session token to return an invalid session token error, etc.
You should also check if you're handling invalid session token error correctly during the migration to prompt the user to login again and therefore obtain a new session token.
I had the same error and neither useMasterKey nor sessionToken worked for me either. :(
Here's my code:
console.log("### attempt 1 sessionToken: " + request.user.getSessionToken());
var p1 = plan.save();
var p2 = request.user.save(null, {sessionToken: request.user.getSessionToken()});
return Parse.Promise.when([p1, p2]).then(function(savedPlan) {
...
}
I see the matching session token in log output:
2016-08-21T00:19:03.318662+00:00 app[web.1]: ### attempt 1 sessionToken: r:506deaeecf8a0299c9a4678ccac47126
my user object has the correct ACL values:
"ACL":{"*":{"read":true},"PC7AuAVDLY":{"read":true,"write":true}}
I also see a bunch of beforeSave and afterSave logs with user being "undefined". not sure whether that's related.
beforeSave triggered for _User for user undefined:
I'm running latest parser-server version 2.2.18 on Heroku (tried it on AWS and results are the same)
function login(logInfo, callback) {
let username = logInfo.email,
password = logInfo.password;
Parse.User.logIn(username, password).then(
(user) => {
if(!user) {
callback('No user found');
} else {
callback(null, user);
}
},
(error) => {
callback(error.message, null);
}
);
}
function update(userInfo, data, callback) {
login(userInfo, (error, user) => {
if(error) {
callback('Can t find user');
} else {
getUpdatedData(user.get('data'), data, (error, updateData) => {
if(error) {
callback(error);
} else {
user.save({data: updateData}, /*{useMasterKey: true}*/ {sessionToken: user.get("sessionToken")}).then(
(test) => {
callback();
}, (err) => {
callback(error.message);
}
);
}
});
}
});
}
For some reason, retrying to use sessionToken worked.
This is not how asynchronous functions work in JavaScript. When createUser returns, the user has not yet been created. Calling user.save kicks off the save process, but it isn't finished until the success or error callback has been executed. You should have createUser take another callback as an argument, and call it from the user.save success callback.
Also, you can't create a user with save. You need to use Parse.User.signUp.
The function returns long before success or error is called.

Categories