I am creating a NodeJS API using Express, PostgreSQL in which I created a controller function which will check the user from the database.
const checkUser = async (req, res) => {
try {
const user = await pool.query('select * from users where email = $1', [req.body.email]);
if (user.rows.length === 0) throw new Error('Error Found');
return res.json("User Found");
} catch (e) {
//======= Output at console ========
//Error: Error Found
console.log(e);
//======== Response Object Received From API ==========
// {
// "msg": "Error Found",
// "Error": {}
// }
res.status(400).send({ msg: 'Error Found', Error: e });
}
};
Consider the situation that the user is not found in the Database so from try block the control passes to the catch block.
1- I am not able to get why the Error thrown from the try block to catch block sends back an empty object as a response. Also, at the same time using console.log prints the correct output value in the console.
2- I want a fix so that I can send e as a response to the User.
The problem is that Error objects are not that easy to serialize (which is intentional). This...
try {
throw new Error('Catch me if you can');
} catch(e) {
console.log(JSON.stringify(e)); // {}
}
... just logs {}, the same as for empty object, because Error objects don't have enumerable properties. And in your case, you don't send that object directly - but make it a property of another object sent to client.
However, there are several ways out of this. If you always need your client to get all the details of an Error, cast that to string (as toString() is overridden):
try {
throw new Error('Catch me if you can');
} catch(e) {
console.log(JSON.stringify(e.toString())); // "Error: Catch me if you can"
}
... or use Error object properties - name and message - as separate fields of your response:
res.status(400).send({
name: e.name || 'Anonymous Error',
msg: e.message || 'no message provided'
});
As sub-approach, you might consider choosing your status code based on type of error you have (always sending 404 for 'not founds', for example).
Related
If no user is found (it's just an example to understand error handling), I want to send a custom message such as
res.status(404).send('no user');
my client never receives that and instead I get:
[AxiosError: Request failed with status code 404]
What am I doing wrong? I cannot find any other solution and have been researching for a while now. Also wonder how I could send a custom status (if no data found it's 404 but what if I want to send 200)?
node express
router.get('/getuser', async (req, res) => {
try {
const user = await User.findOne({_id: req.user._id});
if (!user) {
res.status(404).send('no user');
} else {
res.status(200).send(user)
}
} catch(error) {
res.status(500).send(error)
}
});
frontend
const trycatch = async () => {
try {
const response = await axios.get(example.com/trycatch)
return response.data;
} catch (error) {
console.log(error)
}
}
Responses with a status code of 400 or more are treated as errors by axios. As a consumer, you can only react to that in the catch (error) clause, for example:
catch (error) {
switch (error.response.status) {
case 404: return "no user"; break;
default: return error.response.data;
}
}
But you can influence which statuses cause errors with the validateStatus option of the axios request.
Summary: Either you distribute your code between the try and the catch block, where the try block code handles successes (status < 400) and the catch block handles errors (status ≥ 400). Or you use validateStatus to change what counts as a success and what counts as an error.
I have this block of code:
router.post('/users/login', async (req, res) => {
try {
const { email, password } = req.body
const user = await User.findByCredentials(email, password)
console.log(user) //false
if (!user) {
throw new Error('Login failed! Check authentication credentials')
}
const token = await user.generateAuthToken()
res.status(200).json({ user, token })
} catch (err) {
console.log(err) //Error: Login failed! Check authentication credentials at C:\Users...
res.status(400).json(err)
}
})
It works all fine until there is no Error. When error occur (user is false) in Postman I have only empty object returned {}. Also with res.status(400).json({error: err}) it gives me { "err": {} }.
I want to receive object like this { "error": "Login failed! Check authentication credentials" }
Error objects don't serialize well in JSON because some of their properties are non-enumerable and thus JSON.stringify() does not include them when res.json(err) uses it.
That's why res.json(err) doesn't show what you want.
Possible reasons why non-enumerable properties are not included is that they may contain stack traces and other information that is not meant to be sent back to the client. That's just a by-product of how the Error object works and how JSON.stringify() is implemented and res.json() just inherits those issues and the interaction between the two. I've never understood why the main .message property is non-enumerable as that has never made sense to me, but it is.
There are a number of possible work-arounds:
You could add your own .json2() method that includes all properties of an Error object (even non-enumerable ones).
You could use the Express error handling scheme where you call next(err) and you supply a centralized error handler that gets called and you can do your own serialization of the error response there.
You could make a subclass of Error that populates enumerable properties that will show up in JSON and use that.
Option #3 could look like this:
class RouteError extends Error {
constructor(msg, statusCode = 500) {
super(msg);
// define my own enumerable properties so they
// will show up in JSON automatically
this.error = msg;
this.statusCode = statusCode;
}
}
router.post('/users/login', async (req, res) => {
try {
const { email, password } = req.body
const user = await User.findByCredentials(email, password)
console.log(user) //false
if (!user) {
throw new RouteError('Login failed! Check authentication credentials', 401)
}
const token = await user.generateAuthToken()
res.status(200).json({ user, token })
} catch (err) {
console.log(err) //Error: Login failed! Check authentication credentials at C:\Users...
res.status(err.statusCode).json(err);
}
});
This example would generate this response:
{"error":"Login failed! Check authentication credentials","statusCode":401}
JSON.stringify can't capture non-enumerable object properties as jfriend00 mentions but there are some pretty direct solutions that seem worth mentioning.
One way is to create the correct object yourself using the err.message property, which contains the string provided to the Error constructor:
app.get("/foo", (req, res) => {
try {
throw Error("hello world");
}
catch (err) {
res.status(400).json({error: err.message});
}
});
Another approach if you really can't touch your res.json call is to throw an object set up how you like it. I don't like this because you lose stack trace information (see: 1, 2, 3), but it exists:
app.get("/foo", (req, res) => {
try {
throw {error: "hello world"};
}
catch (err) {
res.status(400).json(err);
}
});
In both cases, the client sees
{
error: "hello world"
}
You can throw an error in case the user is null or empty. Another thread answers a similar question.
Node.js with Express - throw Error vs next(error)
You should use the message property of the Error class for making it enumerable. You don't need to use other property for error message like the below answer using error propery.
I searched for implementation of Error class in 'core-js'
https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/wrap-error-constructor-with-cause.js#:~:text=if%20(message%20!%3D%3D%20undefined)%20createNonEnumerableProperty(result%2C%20%27message%27%2C%20message)%3B
So, if you want message should be displayed in console or delivered by JSON you should use the following code
class CustomError extends Error {
constructor (message) {
// super(message)
super() // avoid message's non-enumerable property
this.name = this.constructor.name
this.message = message;
// needed for CustomError instanceof Error => true
Object.setPrototypeOf(this, new.target.prototype);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
}
}
Then, you can send with this code
const err = new CustomError('Custom Error...');
...
res.send(err);
On my client Vue.js application the console gives me an 404 error, when calling my express API, with an invalid API token.
E.g. calling http://localhost:8081/confirmation/invalidToken
gives, me
xhr.js?b50d:172 GET http://localhost:8081/confirmation/a 404 (Not Found)
Of course it is possible for someone to use that invalid link, so I want to handle these errors instead of printing the error on the console.
If on my express server side, I send the response, like this:
return res.status(404);
Then the console error disappears.
Edit: However this only seems to be the case, because the request is not finished and it's waiting. After a while, the console logs a "net::ERR_CONNECTION_RESET" error.
It's only appearing if I send the response like this:
return res.status(404).send({
message: 'No valid link!',
error,
});
or this:
return res.status(404).send({
message: 'No valid link!',
error,
});
Catching on client side is not working for this problem.
public async sendConfirmation(token: string): Promise<any> {
try {
return axios.get(this.baseURL + '/confirmation/' + token);
} catch (error) {
// tslint:disable-next-line:no-console
console.log(error.response);
}
}
sendConfirmation(this.token)
.then((res) => {
// tslint:disable-next-line:no-console
console.log(res);
if (res.status === 404) {
throw new Error("404");
}
this.data = res.data;
if (res.status === 201) {
this. messagetype = 1;
} else if (res.status === 200) {
this.messagetype = 2;
}
})
.catch((err) => {
this.messagetype = 0;
// tslint:disable-next-line:no-console
// console.log(err );
});
Anyone knows a way to catch that console error, not having to remove the custom message in the server result?
I had same error in heroku in a webpack project with variables environments, i had path of .env file set to local path like c:/project/.env, so the variable of server always return undefined, changing path to relative one fixed the bug:
if (process.env.REACT_APP_ENV === "development") {
require("dotenv").config({
path: ".env.development",
});
}
also check the .env file make sure there is no quotes:
SERVER_URI=https://yourApp.com
and not this:
SERVER_URI="https://yourApp.com"
you have to stringify it later in webpackconfig.
this may help someone or give him a hint.
Well, it's a browser behaviour, that shows the error.
Certain response codes, will in some browsers throw those console messages.
There seems no way to prevent this in a project.
I'm making an app using the nano npm module with nodejs, one of my async functions is intended to create an object in Cloudant but I'm not pretty sure how to handle a Promise.resolve with a callback which is an important part of the response which is supposed my server has to respond.
I do well with creating the document but the next part is to check if there was an error trying to do it, so if there is an error I'd like my server to return an object with the classic error message.
This is my code:
exports.createMeeting = async (body) => {
var response;
object = {"name": "Billy Batson"}
console.log("-------------")
//Trying to insert the document
response = await Promise.resolve(db.insert(object).then((body, err) => {
//This is the part I'm trying to check if the db.insert did fail
if (err) {
response = {
message: 'Failure',
statusCode: '500',
}
console.log(JSON.stringify(response));
} else {
response = {
message: 'Ok',
statusCode: '201',
}
console.log(JSON.stringify(response));
}
}));
}
console.log("******* ", JSON.stringify(response));
return response;
}
If I try to run this code the output is:
-------------
{"message":"Ok","statusCode":"201"}
******* undefined
The first printed object is because the code reached the part where I assign the response object with the status code 201 but the second part doesn't recognize the value of 'response' and the line "return response;" actually doesn't return it, I've confirmed it with postman (it doesn't get a response).
I think the problem here is that I'm not handling correctly the .then() syntax, I've tried changing to a classic callback with:
response = await Promise.resolve(db.insert(object),(body, err) => {
if (err) {
response = {
message: 'Failure',
statusCode: '500',
}
console.log(JSON.stringify(response));
} else {
response = {
message: 'Ok',
statusCode: '201',
}
console.log(JSON.stringify(response));
}
});
But it prints:
-------------
******* {"ok":true,"id":"502c0f01445f93673b06fbca6e984efe","rev":"1-a66ea199e7f947ef40aae2e724bebbb1"}
Which means the code is not getting into the callback (it's not printing 'failure' or 'ok' objects)
What I'm missing here?:(
nano provides promise-based API when a callback is omitted.
This is not how errors are handled in promises. then callback has 1 parameter, there
will be no err.
It's expected that a promise is rejected in case there's an error. Promise.resolve is redundant when there's already a promise and is always redundant with async..await.
It should be:
try {
const body = await db.insert(object);
response = {
message: 'Ok',
statusCode: '201',
}
} catch (err) {
response = {
message: 'Failure',
statusCode: '500',
}
}
I'm using request-promise to get data from an endpoint that I have.
Is it posible to 'capture' a json response in a variable to use it anywhere?
try{
var gamer = '';//variable to capture json data
var options = {
uri: 'http://localhost:4000/gamers/'+gamer._id+'/find',
json: true
};
RequestPromise(options)
.then(function (data) {
gamer = data;//capturing response
})
.catch(function (err) {
console.log("Error saving player data !");
});
.... do something with gamer ....
}catch(err){
res.status(500).send({
message: err.message || 'An error occurred generating player teams !'
});
}
The reason that I need to do this is because actually I don't have access to the database to get that information, so my only option is to consume an API to get information through collections id's.
Your doing a lot of things correctly already. The issue is your gamer variable will get assigned the value you expect first when your promise resolves. There are many ways to skin this cat, but to get you started try performing whatever you want to perform on the gamer variable in .then(), like this:
try{
var gamer = '';//variable to capture json data
var options = {
uri: 'http://localhost:4000/gamers/'+gamer._id+'/find',
json: true
};
RequestPromise(options)
.then(function (data) {
gamer = data;//capturing response
// here is the rigth place perofrm operations on the answer, as this part of the code gets executed after promise reolves. BTW. Then you don't need variable gamer.
})
.catch(function (err) {
console.log("Error saving player data !");
});
// here is not the right place to do something with gamer as this is executed as soon as promise is initialized, not as it resolves. This means your variable will have initial value here
}catch(err){
res.status(500).send({
message: err.message || 'An error occurred generating player teams !'
});
}