I have the following code:
User.getConfByID(userID)
.then((item)=>{
if(item.length == 0){
res.status(400).json({error:"NO_USER_FOUND"})
}else{
if(item[0].token == token){
if((Math.abs(Date.now() - item[0].conf_iat)) > tokenValid){
res.status(401).json({error: "TOKEN_INVALID"})
}else{
return mariaDBTemplates.updateOneRowTemplate("User_confirmation", {confirmed:1}, "user_id", userID)
}
}else{
res.status(401).json({error: "TOKEN_NOT_SAME"})
}
}
})
.then(()=>{
res.status(200).json({success: "CONFIRMED"})
})
.catch((err)=>{
res.status(500).json({error: err.message})
})
You see I have different kinds of error messages with different kinds of status codes. When I run this code, it always gives me this warning:
Error: Can't set headers after they are sent
I think this is because i don't "break" the Promise after sending a response right?. But how can I solve this? Any suggestions?
Cheerio
your problem is with your promise chain. in your first .then, you always set the response with res, but the next .then in the chain tries to set the response again. Note that not returning anything from a promise is the same as return Promise.resolve(undefined);.
here's how I would do it:
User.getConfByID(userID)
.then((item) => {
if(item.length == 0)
return { statusCode: 400, body: { error: "NO_USER_FOUND" } };
else {
if(item[0].token == token) {
if((Math.abs(Date.now() - item[0].conf_iat)) > tokenValid)
return { statusCode: 401, body: { error: "TOKEN_INVALID" } };
else {
//not sure what this returns, but it looks like this is
//what you're trying to return the 200 for
mariaDBTemplates.updateOneRowTemplate("User_confirmation", { confirmed: 1 }, "user_id", userID);
return { statusCode: 200, body: { success: "CONFIRMED" } };
}
} else
return { statusCode: 401, body: { error: "TOKEN_NOT_SAME" } };
}
})
.then((result) => {
res.status(result.statusCode).json(result.body);
})
.catch((err) => {
res.status(500).json({ error: err.message });
});
Also note that returning a value from a promise is the same as returning Promise.resolve(value);, and will continue the promise chain.
Related
I am creating a loop to create/update users using functions with built-in promises:
for (const user of usersjson.users) {
let getuser = getUser(url, okapikey, user[fieldMap.externalSystemId],
'externalSystemId'); //Check if user exists on the server
await getuser
.then(async (data) => {
if (data.users.length != 0) { //If user exists in the array
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array
i++;
} else {
create = createNewUser(url, okapikey, createduser);//Create create function
promises.push(create); //Store function in array
i++;
}
}).catch((err) => {
console.error(err);
});
if (promises.length == 50 || i == usersjson.users.length) {//Run functions in batches of 50
await Promise.allSettled(promises)
.then((responses)=> {
for (const response of responses) { //For each promise response
if (response.status == 'fulfilled') { //If fulfilled
if (response.value.status == 204) {
console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
} else {
if (response.value.status == 201 && response.value.headers.location) {
console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
} else {
console.log(response.value.headers.location);
}
}
} else { //Handle rejections
console.log(`There was an error with the user:${response.value}`);
}
}
}).catch((err)=> {
console.log(err);
});
promises=[]; //Empty Promise array
}
}
async function updateUser(url, token, user, userid)
{
return new Promise((resolve, reject) => {
//Create headers for put request
const options = {
method: "put",
headers: {
'x-okapi-token': token,
'x-okapi-tenant':'tenant',
'Content-type':"application/json"
}
};
//Make API get call
user.id=userid; //Adding the required field ID to the JSON
axios.put(`${url}/users/${userid}`, JSON.stringify(user), options)
.then(response => {
if (response.status == 204) {
resolve(response);
} else {
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
}
}).catch((err) => {
console.error(`Error Code: ${err.response.status}`);
if (typeof err.response.data == 'string') {
console.error(err.response.data);
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
} else if (err.response.data.errors[0].message) {
console.error(`Error Text: ${err.response.data.errors[0].message}`);
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
} else {
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
}
console.log(err.response);
});
});
};
async function createNewUser (url, token, user) {
return new Promise((resolve, reject) => {
//Create headers for put request
const options = {
headers: {
'X-Okapi-token': token,
'Content-type':"application/json"
}
};
//Make API get call
axios.post(`${url}/users`, JSON.stringify(user), options)
.then(response => {
if (response.status == 201) {
resolve(response);
} else {
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
}
}).catch((err) => {
console.error(`Error on ${user.externalSystemId}: ${err}`);
if (err.response.data && typeof err.response.data == 'string') {
console.error(err.response.data);
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
} else if (err.response.data.errors[0].message) {
console.error(`Error Text: ${err.response.data.errors[0].message}`);
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
} else {
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
}
});
});
};
const getUsers = (url,user,password) =>
{
return new Promise((resolve, reject) => {
//Create headers for POST request
const options = {
method: 'post',
headers: {
'Authorization': 'Basic '+Buffer.from(`${user}:${password}`).toString('base64')
}
}
//Make API get call
axios.get(url, options)
.then(response => {
resolve(response.data);
}).catch((err) => {
console.error(err);
reject(err);
});
});
};
The code and loop works fine when every promise is fulfilled, but once a promise is rejected, the loop breaks. I get the error message, for example:
Error on XXX: Error: Request failed with status code 422 Error Text:
User with this username already exists
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing
inside of an async function without a catch block, or by rejecting a
promise which was not handled with .catch(). The promise rejected with
the reason "Error Code: 422: XXX, Error Text: User with this username
already exists, Error Status: Error: Request failed with status code
422".] { }
Looking at the code and error, I believe this comes from the "createNewUser" function.
I'm not sure why the code breaks - I added catches to all the functions, handled rejections, and added catch statements in the main body of the code, but the loop still breaks.
What I need is for the loop to continue as usual even if one function fails (I will later change the log from console.log to an actual log file).
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array
create = createNewUser(url, okapikey, createduser);//Create create function
promises.push(create); //Store function in array
This is inaccurate. You are not storing functions in that array, you actually call the updateUser/createNewUser functions here and store the resulting promises in the array. Then, your loop goes on to sequentially (because of the await) do more getUser operations before actually calling Promise.allSettled on the promises array. In the meantime, some of the promises might already have been rejected without having any handlers attached to them.
This is basically the same problem as discussed in Waiting for more than one concurrent await operation and Any difference between await Promise.all() and multiple await?.
To fix it, collect actual functions that you can execute later in your array:
let functions = [];
for (const user of usersjson.users) {
i++;
try {
const data = await getUser(url, okapikey, user[fieldMap.externalSystemId], 'externalSystemId');
if (data.users.length != 0) {
functions.push(() =>
// ^^^^^
updateUser(url, okapikey, createduser, data.users[0].id)
); // Create update function and store it in array
} else {
functions.push(() =>
// ^^^^^
createNewUser(url, okapikey, createduser)
); // Create create function and store it in array
}
} catch(err) {
console.error(err);
}
if (functions.length == 50 || i == usersjson.users.length) { // in batches of 50
const promises = functions.map(fn => fn()); // Run functions
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const responses = await Promise.allSettled(promises);
for (const response of responses) {
if (response.status == 'fulfilled') {
if (response.value.status == 204) {
console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
} else {
if (response.value.status == 201 && response.value.headers.location) {
console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
} else {
console.log(response.value.headers.location);
}
}
} else {
console.log(`There was an error with the user:${response.value}`);
}
}
functions = []; // empty functions array
}
}
(I've tried to avoid awaiting any .then(…) chains)
I have multiple calls in a process inside in a Route, I find myself obliged to duplicate the "catch" process several times :
Marque.one(createOrder.Order_marque).then(function(m){
Forfaits.oneByIdExt(id).then(function(forfait) {
createOrder["donneur_ordre_id"] = parseInt(forfait[0].donneur_ordre_id)
createOrder["structure_juridique_id"] = parseInt(forfait[0].structure_juridique_id)
OrderEntries.insertOrderEntries(m[0].Marque, idPlan, createUser, createOrder, req.user.id).then(function (data) {
Order.createOrder(createOrder, createUser, req.body.Cart)
.then(function (num_cmd) {
if(forfait[0].tid != null){
OrderTP.insert(createTP).then(function (){
res.status(200).json({return: 200, returnLabel: 'OK', data: {num_cmd}});
})
}else {
res.status(200).json({return: 200, returnLabel: 'OK', data: {num_cmd}});
}
}).catch(function (error) {
res.status(500).json({return: 500, returnLabel: 'KO', data:{}});
});
}).catch(function (error) {
res.status(500).json({return: 500, returnLabel: 'KO', data:{}});
});
}).catch(function (error) {
res.status(500).json({return: 500, returnLabel: 'KO', data:{}});
});
}).catch(function (error) {
res.status(500).json({return: 500, returnLabel: 'KO', data:{}});
});
At the beginning I only put the last one but it does not catch the other errors which follow in the other call, how to avoid such a replication of the same code which is really not beautiful ?
Thank You
use async await and wrap your code in try catch block.
example:
async handlePromise(){
try {
const fist_prmise = await FistPromise();
const second_promise = await SecondPromise();
}catch(e){
console.log(e)
}
}
second promise waiting until first promise resolve
I am sending an HTTP request from angular to a node/express API. How do I get the actual error message I send from the node/express API
I am handling my exceptions well when they are thrown in the API but the moment I try to read the message in Angular I only get the name of the type of error I throw. For example, if I throw a 409 the error received by angular is just "Conflict" and does not contain the details I send. Please look at my code below.
I am sending my request as below
register(user: UserAccount) {
return this.http
.post(`${config.apiUrl}/users/register`, user)
.pipe(
map((res: HttpResponse<Response>) => {
return res;
}))
.pipe(catchError(err => this.errorHandler.handleError(err)));
}
My handle error is as below:
handleError(error: HttpErrorResponse) {
console.log(error);
if (error) {
let errMessage = '';
try {
errMessage = error.message;
} catch (error) {
errMessage = error.statusText;
}
return throwError(errMessage || error || 'Server error');
}
return throwError(error.error || error || 'Server error');
}
This how I am throwing my error when I occurs in my Node/Express API
registerUser (req, res) {
debug(chalk.blue(`*** insert user`))
userRepo
.create(req.body)
.then(user => {
debug(chalk.green(`*** Insert User ok!`))
res.status(200).json({
status: true,
error: null,
user: user
})
})
.catch(err => {
debug(chalk.red(`*** insertUser error: ${util.inspect(err)}`))
if (err['type'] && err['type'] === '409') {
res.status(409).json({
status: false,
error: err['message'],
user: null
})
} else {
res.status(400).json({
status: false,
error: err,
user: null
})
}
})
}
I want to be able to receive the json object with the information about the error but all I am getting when I access the error item is, for example, in the case of raising a 409, I only get 'Conflict'
The response data in case of error is inside the error property of HttpErrorResponse. So you may need to modify the handleError method as below.
handleError(error: HttpErrorResponse) {
console.log(error);
if (error) {
let errMessage = '';
try {
// error.error holds the json response sent from API.
errMessage = JSON.stringify(error.error);
} catch (error) {
errMessage = error.statusText;
}
return throwError(errMessage || error || 'Server error');
}
return throwError(error.error || error || 'Server error');
}
Server-side express code
registerUser(req, res) {
debug(chalk.blue(`*** insert user`))
userRepo
.create(req.body)
.then(user => {
/**
* SUCCESS TO-DO
* .
* .
* .
* .
*/
return res.send(200).json(user)
})
.catch(err => {
/**
* error handler in server
* FAILURE TO-DO
* .
* .
* .
*/
if (err['type'] && err['type'] === '409') {
res.status(409).json({
status: false,
error: 'some 409 type error',
user: null
})
} else {
res.status(400).json({
status: false,
error: 'some 400 type error',
user: null
})
}
})
}
Your angular code
handleError(error: HttpErrorResponse) {
console.log(error);
if (error) {
let errMessage = '';
try {
errMessage = error.message; // <- here is the problem
} catch (error) {
errMessage = error.statusText;
}
return throwError(errMessage || error || 'Server error');
}
return throwError(error.error || error || 'Server error');
}
Firstly, if I may rename your HttpErrorResponse object as http_error as it is too generic. However, the trick lies in reading from the error key of the http_error object, and not from the message key.
Client-side error handler(angular)
handleError(http_error: HttpErrorResponse) {
// {status: false, error: 'some 409/400 type error', user: null }
const errMessage = http_error.error;
/* TO-DO STUFF */
return throwError(errMessage || 'Server error');
}
I have created a nodejs/express api, which is a wrapper around another api.
I am making async calls with try and catch. I am wondering in the inner async call if it catches an error will this error be caught by the outer async call.
The transaction.controller is my api which calls the transaction.repository to an external api. The function getTransactionResult is throwing an error in the catch part. However, this does NOT loop back to the catch part of my show function.
router.ts
this.router.get('/:id', this.controller.show.bind(this.controller));
transaction.controller.ts
public async show(req: Request, res: Response): Promise<any> {
const params = req.params;
const token = req.headers.token;
let transaction;
try {
transaction = await this.transactionRepo.getTransactionResult(params.id, token);
console.log("instead carries on here"); // 2
if (transaction.success === false) {
return res.status(404).json({
success: false,
status: 404,
data: transaction,
message: "Failed to retrieve transaction result"
});
}
return res.status(200).json({
success: true,
status: 200,
data: transaction,
message: "Successfully retrieved transaction result"
});
} catch (err) {
//console.log("Does not call this");
return res.status(500).json({
success: false,
status: 400,
data: err,
message: "The server threw an unexpected error",
});
}
transaction.repository.ts
public async getTransactionResult(transactionId: string, token: string) {
const url = this.config.api_url + "/api/transactions/" + transactionId + "/summary";
const options = {
uri: url,
headers: {
'token': token
},
body: {
},
json: true,
resolveWithFullResponse: true
}
let result;
try {
result = await request.get(options);
if (result.statusCode !== 200) {
return { "success": false, data: result }
}
return { "success": true, data: result }
} catch (err) {
console.log("CAUGHT ERROR"); // 1
return err;
}
}
You need to rethrow the error not return it.
By doing return err you are resolving the Promise which mean the async operation succeeded. Hence why your out try-catch in transaction.controller.ts does not catch anything.
Either:
Don't catch the error and let it bubble up
Rethrow the error
I send data from my input fields to my api:
$.ajax({
url: '/api/login',
type: 'GET',
dataType: 'json',
ContentType: 'application/json',
data: {formData},
success: (data) => {
console.log('SUCCESS')
console.log(data)
this.setState({
isInProcess: false
})
},
error: (jqXHR) => {
console.log(jqXHR)
console.log('ERROR')
this.setState({isInProcess: false})
}
})
on my server-side I have a function to see if I have required user in db:
async function findUser(data) {
try {
const user = await User.findOne({email: data.email,
password: data.password})
console.log('User was found')
return { user }
} catch (err) {
console.log('error', err)
throw err
}
}
which will be executed here:
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
return res.json(foundUserData)
} catch (err) {
return res.status(400).json(err)
}
})
It works fine, but if a user wasn't found in db i sends success anyway. Why?
await findUser(data.formData) won't throw error, return either null or user object. You may check something following
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
if(foundUserData && foundUserData.user) {
return res.json(foundUserData)
} else {
return res.status(400).json({message: 'User not found'});
}
} catch (err) {
return res.status(500).json(err)
}
})
It sends success because none of your queries error'ed, just because it didn't find anything does not mean that the query failed because it obviously succeeded in finding out if what ever you're looking for exists or not.
To send an error in case of not found you need to check if response is empty in which case you want to send error
When no user is find you get a null value. You may try to put more logic on your success parameter with that for example:
success: function (data) {
if(!!data && data != null) {
alert('Success');
} else {
alert('No data');
}
}