I know this question is being repeated again. I've already looked at other solutions and tried them already. I'd appreciate your help in debugging the issue.
I've a separate errorHandler as a middleware.
error-handler.ts:
import express, { Request, Response, NextFunction } from "express";
export const errorHandler = (
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
console.log('Something went wrong');
res.status(400).send({
message: err.message
});
}
index.ts:
import express, { Request, Response, NextFunction } from 'express';
import { json } from 'body-parser';
import { currentUserRouter } from './routes/current-user';
import { signinRouter } from './routes/signin';
import { signoutRouter } from './routes/signout';
import { signupRouter } from './routes/singup';
import { errorHandler } from './middlewares/error-handlers';
const app = express();
app.use(json());
app.use(currentUserRouter);
app.use(signinRouter);
app.use(signoutRouter);
app.use(signupRouter);
app.use(errorHandler);
app.listen(3000, () => {
console.log('Listening on port 3000!!!');
})
When an Error is thrown, the Express is not catching it.
signup.ts:
import express, { Request, Response } from "express";
const router = express.Router();
import { body, validationResult } from "express-validator";
const bodyValidation = [
body('email')
.isEmail()
.withMessage('Email must be valid'),
body('password')
.trim()
.isLength({ min: 4, max: 20})
.withMessage('Password must be between 4 and 20 characters')
];
router.post('/api/users/signup', bodyValidation, async (req: Request, res: Response) => {
const errors = validationResult(req);
if(!errors.isEmpty()) {
throw new Error('Invalid email or password');
}
console.log('Creating a user...');
res.send({});
});
export { router as signupRouter }
I can see Error: Invalid email or password in the terminal being thrown. But Express is not catching the error being thrown.
Express doesn't pay any attention to the return value of your route handler. You're passing an async function to router.post, which means that your function will return a promise to report its completion, rather than reporting it in the usual synchronous way. So any error thrown within the function is reported via the promise — which Express ignores.
In general, it's not a good idea to pass an async function to something as a callback if that something (Express, in this case) doesn't support handling the Promise the function returns.
In your example, you don't have any code using await, so you could just remove the async.
If you have code using await that you left out to keep the question brief (which was perfectly reasonable), you might make that an inner function you call and use the promise from:
router.post("/api/users/signup", bodyValidation, (req: Request, res: Response) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
throw new Error("Invalid email or password");
}
console.log("Creating a user...");
(async () => {
// ...your code using `await` here...
// You're all done, send the result
res.send({});
})().catch((error) => {
// ...send error response...
});
});
Note how that code catches promise rejection in order to send an error response — you wouldn't want to just leave the promise rejection unhandled.
Another way you can do that is to ensure that the entire body of that inner function is in a try/catch:
router.post("/api/users/signup", bodyValidation, (req: Request, res: Response) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
throw new Error("Invalid email or password");
}
console.log("Creating a user...");
(async () => {
try {
// ...your code using `await` here...
// You're all done, send the result
res.send({});
} catch (error) {
// ...send error response...
}
})();
});
Related
How do I make express.js handle ERR_UNHANDLED_REJECTION in async route handlers? For example, how do I make it so that the code below doesn't crash on request:
import express, {NextFunction, Request, Response} from "express";
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', async () => {
await fails();
});
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({err})
});
app.listen(9999);
Try Koa as it's already async.
Otherwise, create a generic wrapper function that can turn an async function into the required express response.
import express, {NextFunction, Request, Response} from "express";
function genHandler(responseHandler: Function) {
return async function theHandler(req: Request, res: Response, next: NextFunction) {
try {
const res = await responseHandler(req, res, next)
res.send(res)
}
catch (error) {
next(error)
}
}
}
async function ok() {
return 'ok'
}
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', genHandler(fails));
app.get('/ok', genHandler(ok));
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({err})
});
app.listen(9999);
The handler generator can hide a lot of repeated async request/response complexity, like express middleware does for the non async world.
You can use try-catch in you route.
import express, {
NextFunction,
Request,
Response
} from "express";
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', async() => {
try {
await fails();
} catch (error) {
// You catch the error in here
}
});
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({
err
})
});
app.listen(9999);
you must use try and catch block.
import express, {NextFunction, Request, Response} from "express";
async function fails() {
throw `Test`;
}
const app = express();
app.get('/', async (req, res, next){
try{
await fails();
}
catch(err){
//you can also create a global function to handle errors
return res.send(err)
}
});
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.log(`err`, err);
res.send({err})
});
app.listen(9999);
When testing the default error handler of an express application, it results in a timeout. The function looks as follows:
const createApp = (underlyingFunction) => {
const app = express()
app.get('/my-endpoint', async (req, res) => {
await underlyingFunction()
res.send({ success: true })
})
const errorHandler: ErrorRequestHandler = (error, req, res, next) => {
console.error('Unhandled exception');
console.error(error);
console.error(error.stack);
res.status(500).send({
message: 'Oh dear',
});
// next()
}
app.use(errorHandler)
return app;
}
And the test looks as follows:
test('error should be handled and return 500', async () => {
underlyingFunction.mockImplementation(() => {
throw new Error('Something went wrong')
})
const app = createApp(underlyingFunction)
const response = await request(app).get('/my-endpoint')
expect(response.status).toBe(500)
})
When running the test, I get the following error:
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
What could be causing this?
For express V4, from the Error Handling#Catching Errors doc, we know:
For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them.
Although the underlyingFunction of the mock in the test case is synchronous, but in the route, the async/await syntax converts this route handler into asynchronous code.
So, you need to use try...catch statement to catch the error raised from underlyingFunction function. And pass the error to the next function. express will route the request to the error handler middleware with that error.
E.g.
app.ts:
import express from 'express';
import { ErrorRequestHandler } from 'express-serve-static-core';
export const createApp = (underlyingFunction) => {
const app = express();
app.get('/my-endpoint', async (req, res, next) => {
try {
await underlyingFunction();
res.send({ success: true });
} catch (error) {
next(error);
}
});
const errorHandler: ErrorRequestHandler = (error, req, res, next) => {
console.error('Unhandled exception');
res.status(500).send({ message: 'Oh dear' });
};
app.use(errorHandler);
return app;
};
app.test.ts:
import request from 'supertest';
import { createApp } from './app';
describe('68923821', () => {
test('error should be handled and return 500', async () => {
const underlyingFunction = jest.fn().mockImplementation(() => {
throw new Error('Something went wrong');
});
const app = createApp(underlyingFunction);
const res = await request(app).get('/my-endpoint');
expect(res.status).toEqual(500);
});
});
test result:
PASS examples/68923821/app.test.ts (9.128 s)
68923821
✓ error should be handled and return 500 (49 ms)
console.error
Unhandled exception
15 |
16 | const errorHandler: ErrorRequestHandler = (error, req, res, next) => {
> 17 | console.error('Unhandled exception');
| ^
18 | res.status(500).send({ message: 'Oh dear' });
19 | };
20 |
at errorHandler (examples/68923821/app.ts:17:13)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.661 s
import { getCustomRepository } from "typeorm";
import { NextFunction, Request, Response } from "express";
import { UserRepository } from "../repositories/user-repository";
export class UserController {
private userRepository = getCustomRepository(UserRepository);
all = async (req: Request, res: Response, next: NextFunction) => {
// return this.userRepository.find();
let users = await this.userRepository
.createQueryBuilder('users')
.addSelect('users.passworda')
.getMany()
.catch(err => {
console.log("Reached1");
return next(err);
})
console.log("Reached2");
}
}
I was expecting Reached2 will never be called if there is an exception and only Reached1 gets called.
But both Reached1 and Reached2 gets called in sequence.
To simulate a DB error, i used passworda instead of password db field
If the promise returned by getMany() get rejected (an exception was thrown) it get catched and next() is called:
.getMany()
.catch(err => {
console.log("Reached1");
return next(err);
});
The resulting promise returned by catch itself is resolved successfully because the exception was handled before with a resulting value of undefined. So let users will hold undefined and the code execution continues to console.log("Reached2");.
The following code will behave as you have expected:
all = async (req: Request, res: Response, next: NextFunction) => {
try {
let users = await this.userRepository
.createQueryBuilder('users')
.addSelect('users.passworda')
.getMany();
console.log("Reached2");
} catch (err) {
console.log("Reached1");
return next(err);
}
}
Using NodeJS 10.13.0, ExpressJS 4.16.4...
Got a controller handling a route that looks like this:
import { Request, Response, NextFunction } from 'express';
import braintree from 'braintree';
import config from '../../server.config';
export function index(req: Request, res: Response, next: NextFunction): void {
if(!config.braintree) throw new Error('Braintree configuration missing.');
const gateway: any = braintree.connect(config.braintree); // synchronous
return gateway.clientToken.generate({})
.then(response => res.send(response.clientToken))
.catch(next) // operational error unless config.braintree was changed
}
Reading the ExpressJS docs on error handling, I'm wondering if I'm following best practices - throwing an Error for the synchronous portion and passing the error to next() in the catch for the asynchronous portion.
Any recommendations for improvement?
Considering that promises are in use, both synchronous and asynchronous errors can be consistently handled with async function:
export async function index(req: Request, res: Response, next: NextFunction) {
try {
if(!config.braintree)
throw new Error('Braintree configuration missing.');
const gateway: any = braintree.connect(config.braintree);
const response = await gateway.clientToken.generate({})
res.send(response.clientToken);
} catch (err) {
next(err);
}
}
Since Express doesn't support promises, async function body should be wrapped with try..catch. Considering that try..catch is common for all async middleware functions, it could be moved to a helper:
const asyncMiddleware = (middleware: RequestHandler) => async (req: Request, res: Response, next: NextFunction) => {
try {
await middleware(req, res, next);
} catch (err) {
next(err)
}
};
Which is used like:
export const index = asyncMiddleware(async (...) => {...});
Can someone expound on the times when it's appropriate in a node.js Express app to throw an error like so:
throw new Error('my error');
or to pass this error on via the callback usually labelled 'next' like so:
next(error);
and could you please explain what each of them will do in the context of an Express app?
for example, here is an express function dealing with URL parameters:
app.param('lineup_id', function (req, res, next, lineup_id) {
// typically we might sanity check that user_id is of the right format
if (lineup_id == null) {
console.log('null lineup_id');
req.lineup = null;
return next(new Error("lineup_id is null"));
}
var user_id = app.getMainUser()._id;
var Lineup = app.mongooseModels.LineupModel.getNewLineup(app.system_db(), user_id);
Lineup.findById(lineup_id, function (err, lineup) {
if (err) {
return next(err);
}
if (!lineup) {
console.log('no lineup matched');
return next(new Error("no lineup matched"));
}
req.lineup = lineup;
return next();
});
});
In the line commented "//should I create my own error here?"
I could used "throw new Error('xyz')", but what exactly would that do? Why is it usually better to pass the error to the callback 'next'?
Another question is - how do I get "throw new Error('xyz')" to show up in the console as well as the browser when I am in development?
In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next', also an error handler needs to be defined so that all the errors passed to 'next' can be handled properly.
http://expressjs.com/en/guide/error-handling.html
Throwing an error inside a callback doesn't work:
app.get('/', function (req, res) {
fs.mkdir('.', (err) => {
if (err) throw err;
});
});
But calling next works:
app.get('/', function (req, res, next) {
fs.mkdir('.', (err) => {
if (err) next(err);
});
});
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:
app.get('/', function (req, res) {
throw new Error('BROKEN') // Express will catch this on its own.
})
For those who prefer throwing errors, here is a workaround decorator:
export function safeThrow(
target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<(req: Request, res: Response, next: NextFunction) => Promise<any>>) {
const fun = descriptor.value;
descriptor.value = async function () {
try {
await fun.apply(this, arguments);
} catch (err) {
arguments[2](err);
}
};
}
#safeThrow
private async get(req: Request, res: Response, next: NextFunction) {
throw { status: 404, message: 'Not supported' }
}