Linter · Rules
oxc/no-async-endpoint-handlers
Disallow async functions as Express route handlers and middleware
Express 4 ignores the promise that an async handler returns. If the handler throws, or awaits something that rejects, your error middleware never runs. The request hangs, and the unhandled rejection can crash the process.
The rule checks .get(), .post(), .put(), .patch(), .delete(), .head(), .options(), .all(), and .use() on any object. A function written in the call is only reported if its parameters have the usual names, such as (req, res) or (err, req, res, next). A handler passed by name is looked up in the same file.
Configure
Off by default. To turn it on:
[lint.rules]
"oxc/no-async-endpoint-handlers" = "error" # or "warn"
Every option, set to its default:
[lint.rules]
"oxc/no-async-endpoint-handlers" = ["error", {
allowedNames = [], # list of strings
}]
Examples
Reported:
app.get("/users/:id", async (req, res) => {
const user = await db.findUser(req.params.id);
res.json(user);
});
async function listUsers(req, res) {
res.json(await db.allUsers());
}
router.get("/users", listUsers);
Not reported:
app.get("/users/:id", (req, res, next) => {
db.findUser(req.params.id)
.then(user => res.json(user))
.catch(next);
});
A handler wrapped in a call isn't reported, so a helper that passes rejections to next works too:
const wrap = handler => (req, res, next) => handler(req, res, next).catch(next);
app.get(
"/users/:id",
wrap(async (req, res) => {
res.json(await db.findUser(req.params.id));
}),
);
Options
| Option | Default | What it does |
|---|---|---|
allowedNames | [] | Names of async functions you can use as handlers. |
async function listUsers(req, res) {
res.json(await db.allUsers());
}
router.get("/users", listUsers);
The name is the function's own name. An arrow function doesn't have one, so it's reported even if the variable that holds it is in the list:
const listUsers = async (req, res) => {
res.json(await db.allUsers());
};
router.get("/users", listUsers);
When not to use it
Express 5 passes a rejected promise to next by itself, so async handlers are safe there.