'TypeScript/Eslint throwing a 'Promise returned' error on a Express Router async route

I have the following endpoint setup to reset a database after test runs:

import { getConnection } from 'typeorm';
import express from 'express';
const router = express.Router();

const resetDatabase = async (): Promise<void> => {
  const connection = getConnection();
  await connection.dropDatabase();
  await connection.synchronize();
};

// typescript-eslint throws an error in the following route:
router.post('/reset', async (_request, response) => {
  await resetTestDatabase();
  response.status(204).end();
});

export default router;

The entire route since async is underlined with a typescript-eslint error Promise returned in function argument where a void return was expected.

The app works perfectly but I'm not sure if I should be doing a safer implementation or just ignoring/disabling Eslint for this one. Any idea of what's wrong with that code?



Solution 1:[1]

It seems you are using the no-misused-promises rule which states that you cannot return Promise<void> in a place where void is expected.

This means that you cannot return Promise<void> from your Express handler because the return type of RequestHandler from the library specifies that the return type should be void. I would suggest that you change it to return Promise<Response> by adding a simple return keyword:

import { getConnection } from 'typeorm';
import express from 'express';
const router = express.Router();

const resetDatabase = async (): Promise<void> => {
  const connection = getConnection();
  await connection.dropDatabase();
  await connection.synchronize();
};

// typescript-eslint throws an error in the following route:
router.post('/reset', async (_request, response) => {
  await resetTestDatabase();
  return response.status(204).send();  // <----- return added here
});

export default router;

The other option would be to avoid using async/await:

router.post('/reset', (_request, response) => {
  resetDatabase().then(() => response.status(204).send());
});

Solution 2:[2]

I found a solution that doesn't involves using then() and let you use the abstraction of async without getting cursed by the eslint, there's two solutions (but i recommend more the second one)

First Solution: Using "inside async"

This is basic using a async inside the void like this:

router.post('/reset', (_request, response) => {
    (async () => {
        await resetTestDatabase();
        response.status(204).end();
    })()
});

Second Solution (recommended): "Type Overlap"

The second option is to you use it as an async, as Always, but say "hey TypeScript, nothing is wrong here hehe" with the "as" keyword

import { RequestHandler } from 'express'

router.post('/reset', (async (_request, response) => {
    await resetTestDatabase();
    response.status(204).end();
}) as RequestHandler);

Solution 3:[3]

After also going around the houses a little, here is an update:

Express 5 now handles this correctly (and is now in beta). Confirmation here from the Express maintainers:

Typings however are not yet (at the time of writing) updated/available for Express 5 so the error still displays, despite being resolved. Follow here on the DefinitelyTyped repo for conversation on updates.

In the meantime, as suggested already by KuryKat already, handling with the RequestHandler should be safe in Express 5.

import { RequestHandler } from 'express'

router.post('/reset', (async (_request, response) => {
    await resetTestDatabase();
    response.status(204).end();
}) as RequestHandler);

In Express 4 however, better to handle the error either with an explicit .catch(...):

app.get("/manifest.webmanifest", (_req, res) => {
  getManifest(pwaOptions)
    .then(result => res.send(result))
    .catch(error => res.status(500).send('Unexpected error'))
});

Or use an async handler:

express-async-handler

express-async-router

See discussion here regarding Express 4 for reference.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 KuryKat
Solution 3 Maggie