Supporting Promises in Express Middleware
May 21, 2019
Express doesn't support promises or async/await in middleware or routes. In the below example, the Express endpoint will never send a response because of an unhandled promise rejection.
const app = require('express')();
app.get(async function routeHandler(req, res) {
// Will throw an error because `req.params.bar` is undefined
req.params.bar.toString();
// Request will hang forever because `res.json()` never gets called.
res.json({ test: 42 });
});
const server = await app.listen(3000);
// Will time out. If not for the `timeout` option, would hang forever.
const err = await axios.get('http://localhost:3000', { timeout: 300 }).
catch(err => err);
err.message; // "timeout of 300ms exceeded"
In order to make sure your Express app doesn't hang forever, you need to make sure your middleware functions call next()
and your route handlers call res.send()
or res.json()
. The easiest way to do this is with the @awaitjs/express
library.
const app = require('express')();
const { addAsync } = require('@awaitjs/express');
addAsync(app);
// @awaitjs/express adds a `getAsync()` function to Express
app.getAsync(async function routeHandler(req, res) {
// The `getAsync()` function knows to look out for promise rejections
req.params.bar.toString();
res.json({ test: 42 });
});
const server = await app.listen(3000);
const err = await axios.get('http://localhost:3000').
catch(err => err);
err.message; // "Request failed with status code 500"
If you don't want to use an outside library, you can handle errors yourself. With async/await, you can wrap your logic in a try/catch
to make sure errors are caught. Just make sure your catch
block doesn't throw an error.
const app = require('express')();
app.get(async function routeHandler(req, res) {
// Wrap your route handler logic in a try/catch, and make sure
// to respond if an unexpected error occurs.
try {
req.params.bar.toString();
res.json({ test: 42 });
} catch (err) {
res.status(500).json({ message: err.message });
}
});
const server = await app.listen(3000);
const err = await axios.get('http://localhost:3000').
catch(err => err);
err.message; // "Request failed with status code 500"
If you're using promise chaining, you should use the Promise#catch()
function.
const app = require('express')();
app.get('/', function routeHandler(req, res) {
return Promise.resolve().
then(() => req.params.bar.toString()).
then(() => res.json({ test: 42 })).
// Make sure you call `.catch()` on your promise to handle errors!
catch(err => res.status(500).json({ message: err.message }));
});
const server = await app.listen(3000);
const err = await axios.get('http://localhost:3000').
catch(err => err);
err.message; // "Request failed with status code 500"
Want to become your team's Express expert? There's no better way to really grok a framework than to write your own
clone from scratch. In 15 concise pages, this tutorial walks you through how to write a simplified clone of Express
called Espresso.
Get your copy!
Espresso supports:
Get the tutorial and master Express today!
Espresso supports:
- Route handlers, like `app.get()` and `app.post()`
- Express-compatible middleware, like `app.use(require('cors')())`
- Express 4.0 style subrouters
Get the tutorial and master Express today!
Did you find this tutorial useful? Say thanks by starring our repo on GitHub!