Express 5 Awaits Your Promises — Express 4 Didn't
Async Route Handlers
Express 5 supports async/await natively. Express 4 didn't — unhandled rejections crashed the request.
What you'll learn
- Write async handlers in Express 5
- Use express-async-handler in Express 4
- Handle errors thrown by async code
In Express 4, an async handler that threw would crash without hitting your error middleware. Express 5 fixed it.
Express 5
app.get("/users/:id", async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) {
res.status(404).json({ error: "not found" });
return;
}
res.json(user);
}); Throws or rejections propagate to your error middleware automatically.
app.get("/users/:id", async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) throw new NotFoundError("user");
res.json(user);
});
// Centralized error handler catches it:
app.use((err, req, res, next) => {
if (err instanceof NotFoundError) {
return res.status(404).json({ error: err.message });
}
res.status(500).json({ error: "internal" });
}); Express 4 — express-async-handler
In Express 4, wrap each async handler:
npm install express-async-handler import asyncHandler from "express-async-handler";
app.get("/users/:id", asyncHandler(async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(user);
})); Or roll your own:
const asyncH = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get("/users/:id", asyncH(async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(user);
})); Don’t Forget to await
Subtle bug:
// missing await — error from createUser is silently swallowed
app.post("/users", async (req, res) => {
db.users.create(req.body); // no await!
res.status(201).end();
}); Always await async calls. ESLint’s no-floating-promises rule
catches this.
Async Middleware Too
Middleware can also be async — same rules:
async function requireAuth(req, res, next) {
try {
req.user = await verifyToken(req.headers.authorization);
next();
} catch (err) {
res.status(401).json({ error: "invalid token" });
}
} Or, even cleaner — throw and let the error middleware handle it.
Route Order Matters →