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);
Related
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...
}
})();
});
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 (...) => {...});
I was looking at a blog https://www.acuriousanimal.com/2018/02/15/express-async-middleware.html on how to handle do await/async in Typescript express routes.
I liked the idea of using the Higher Order Functions to avoid code redundancy, but
am not sure how to do it in TypeScript
Is there any other better way TypeScript recommends for this kind of scenario
Sample Code snippet:
https://gist.github.com/kameshsampath/e550d5bf19feb9b59d0ec1871e59b53a
I believe you want to remove the catch from the following code into a higher order function:
app.get("/api/frames", async (req: Request, res: Response, next: any) => {
//TODO move this to higher order function
try {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
} catch (err) {
res.status(404).send(err);
}
app.get("/api/frames", async (req: Request, res: Response, next: any) => {
//TODO move this to higher order function
try {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
} catch (err) {
res.status(404).send(err);
}
});
This does that:
const asyncHandler = fn => (req, res, next) =>
Promise
.resolve(fn(req, res, next))
.catch((err)=>res.status(404).send(err);)
app.get("/api/frames", asyncHandler(async (req: Request, res: Response, next: any) => {
const qCollection = await loadCollection("frames", db);
const docs = qCollection.find();
if (docs) {
return res
.contentType("json")
.status(200)
.send(docs);
}
}));
I have a file where multiple export functions that are invoked from api's and each of these methods will make some get/post inside the function.
so my questions is for Promise.all that looks redundant to me is there better approach to achieve this using one private method handler that can be implemented or invoked from each of those export function and return response.
main.ts
export function getUser(req: Request, res: Response) {
const p1 = Promise.resolve("data1");
const p2 = Promise.resolve("data2");
Promise.all([p1,p2])
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
export function getRanks(req: Request, res: Response) {
const p1 = Promise.resolve("data3");
const p2 = Promise.resolve("data4");
Promise.all([p1,p2])
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
You can do exactly what you wrote - create function that does the general handling.
export function getUser(req: Request, res: Response) {
const p1 = Promise.resolve("data1");
const p2 = Promise.resolve("data2");
sendResponse(req, res, [p1,p2]);
}
export function getRanks(req: Request, res: Response) {
const p1 = Promise.resolve("data3");
const p2 = Promise.resolve("data4");
sendResponse(req, res, [p1,p2]);
}
function sendResponse(req, res, promises) {
Promise.all(promises)
.then(function(results) {
res.json(results);
})
.catch(function(e) {
console.log(e)
});
}
PS: You should have some res handling in .catch (res.end() or res.status(500); res.json({error: e})) otherwise the request will be hanging on for 30-90sec (based on your settings)
In case p1, etc. promises are really created Promise.resolve, it could be omitted; Promise.all accepts regular values.
It could be written with async..await in more succinct manner:
export async function getUser(req: Request, res: Response) {
...
try {
const results = await Promise.all([p1, p2]);
res.json(results);
} catch (e) {
console.log(e)
}
}
At this point functions don't need to be DRYed any further.