I have a register function inside my Express application to create a new user. Inside this function there are a few tasks: create the user in Auth0, send an email, send a response to the client.
I want to be able to catch the errors coming from Auth0 or Postmark to send back specific errors to the client and log them to the console. I though I could achieve this by adding a catch to an await function (I want to avoid a waterfall of .then()
and .catch()
blocks). This sends the error to the client but doesn't stop the code from executing. The email part is still trying to execute while the user object is undefined and I'm getting the error Cannot set headers after they are sent to the client
.
How can I fix this by keeping the async/await
functionality and keep the seperate error handling for each action?
Register function
export const register = asyncHandler(async (req, res, next) => {
// Create user in Auth0
const user = await auth0ManagementClient.createUser({
email: req.body.email,
password: generateToken(12),
verify_email: false,
connection: 'auth0-database-connection'
}).catch((error) => {
const auth0_error = {
title: error.name,
description: error.message,
status_code: error.statusCode
}
console.log(auth0_error);
if(error.statusCode >= 400 && error.statusCode < 500) {
return next(new ErrorResponse('Unable to create user', `We were unable to complete your registration. ${error.message}`, error.statusCode, 'user_creation_failed'));
} else {
return next(new ErrorResponse('Internal server error', `We have issues on our side. Please try again`, 500, 'internal_server_error'));
}
});
// Send welcome mail
await sendWelcomeEmail(user.email)
.catch((error) => {
const postmark_error = {
description: error.Message,
status_code: error.ErrorCode
}
console.log(postmark_error);
if(error.statusCode >= 400 && error.statusCode < 500) {
return next(new ErrorResponse('Unable to send welcome email', `We were unable to send a welcome email to you`, error.statusCode, 'welcome_email_failed'));
} else {
return next(new ErrorResponse('Internal server error', `We have issues on our side. Please try again`, 500, 'internal_server_error'));
}
});
res.status(201).json({
message: 'User succesfully registered. Check your mailbox to verify your account and continue the onboarding.',
data: {
user
}
});
});
asyncHandler.js
const asyncHandler = fn => ( req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
export default asyncHandler;
CodePudding user response:
I'd use try/catch blocks, but declare the user variable outside the try scope.
async function handler(req, res, next) {
let user;
try {
user = await auth0ManagementClient.createUser(...);
} catch (error) {
return next(new ErrorResponse(...));
}
try {
await sendWelcomeEmail(user.email);
} catch (error) {
return next(new ErrorResponse(...));
}
res.status(201).json(...);
}
CodePudding user response:
return
will only terminate the current function. Here, the function that gets terminated by the return
is the .catch()
callback.
In your example and if you want to stick to using Promise.then().catch()
you can check for the user
value as the catch()
callback will return its value in it.
The easier way would be to use try/catch blocks to interrupt the whole controller with the return
statement.