# 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.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml bunfig.toml
[lint.rules]
"oxc/no-async-endpoint-handlers" = "error" # or "warn"
```

Every option, set to its default:

```toml bunfig.toml
[lint.rules]
"oxc/no-async-endpoint-handlers" = ["error", {
  allowedNames = [], # list of strings
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```js
app.get("/users/:id", async (req, res) => {
  const user = await db.findUser(req.params.id);
  res.json(user);
});
```

{/* bad */}
```js
async function listUsers(req, res) {
  res.json(await db.allUsers());
}

router.get("/users", listUsers);
```

Not reported:

{/* good */}
```js
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:

{/* good */}
```js
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. |

{/* good {"options": [{"allowedNames": ["listUsers"]}]} */}
```js
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:

{/* bad {"options": [{"allowedNames": ["listUsers"]}]} */}
```js
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.
