Consider a scenario where there is an API endpoint /register
, designed to register new users in an application. The function utilized is asynchronous, as an attempt is made to incorporate an asynchronous process within an AuthController when performing password hashing:
authRouter.post('/register', async (req: Request, res: Response, next: NextFunction) => {
const { username, password } = req.body;
const hashedPassword = await AuthController.hashPassword(password, next);
// execute additional tasks
Within the AuthController, error handling is demonstrated by throwing an error as shown below:
const hashPassword = async (password: string, next: NextFunction): Promise<void> => {
try {
throw new Error('test error');
//return await bcrypt.hash(password, 10);
} catch(err) {
next(err);
}
}
During testing of the API, the server crashes unexpectedly, with the following log message:
[1] Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[1] at new NodeError (node:internal/errors:400:5)
[1] at ServerResponse.setHeader (node:_http_outgoing:663:11)
[1] at ServerResponse.header (C:\Users\renet\Desktop\git\rapidhcm-monorepo\api\node_modules\express\lib\response.js:794:10)
[1] at ServerResponse.send (C:\Users\renet\Desktop\git\rapidhcm-monorepo\api\node_modules\express\lib\response.js:174:12)
[1] at ServerResponse.json (C:\Users\renet\Desktop\git\rapidhcm-monorepo\api\node_modules\express\lib\response.js:278:15)
[1] at ServerResponse.send (C:\Users\renet\Desktop\git\rapidhcm-monorepo\api\node_modules\express\lib\response.js:162:21)
[1] at C:\Users\renet\Desktop\git\rapidhcm-monorepo\api\dist\src\routes\auth\auth.js:72:25
[1] at Generator.next (<anonymous>)
[1] at fulfilled (C:\Users\renet\Desktop\git\rapidhcm-monorepo\api\dist\src\routes\auth\auth.js:28:58)
[1] at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
[1] code: 'ERR_HTTP_HEADERS_SENT'
[1] }
[1]
[1] Node.js v18.13.0
The malfunction appears to be triggered by the failure of the function to halt at the catch block, but rather proceeding to the '// do something else' segment where it endeavors to send a response. Although one potential solution could involve enclosing the try {} catch{} block within the router function scope, this approach is dismissed due to two primary reasons:
- From a design perspective, consolidating try {} catch{} blocks into separate functions enhances code readability.
- A single encompassing try {} catch{} block for the entire function would be required since variables cannot be accessed outside try{} catch{} scopes.
EDIT: To provide clarity, here is the '// do something else'' code block snippet:
try {
await User.create({ username, password: hashedPassword ?? 's' });
res.send({ username, password, hashedPassword });
} catch (error) {
console.error('Something went wrong when adding a new user', error);
res.status(400).send({
message: 'User already exists in the database',
});
}