Code Continues After res.send?

· 2 min read · 256 Words · -Views -Comments

Express is one of the most popular Node.js frameworks. Its middleware model and routing make it easy to get started, but that flexibility raises the bar for maintainability. Small oversights can create bugs that only surface later.

Here’s a simple example:

router.get('/c', function (req, res) {
  const conf2 = require('../conf');
  res.send({ value: conf2.a });
  console.log('hello');
});

Run it and you’ll see “hello” in the console even after res.send. To stop execution, return immediately:

router.get('/c', function (req, res) {
  const conf2 = require('../conf');
  return res.send({ value: conf2.a });
  console.log('hello');
});

Now the handler exits. The Express docs omit return because their examples end with res.send; nothing follows. Once your logic becomes more complex, though, you must return when short-circuiting.

Consider this download endpoint. Without the return, the remainder of the callback would still run after sending a 404:

router.get('/download', function (req, res) {
  const key = req.query.key;
  const ua = parser(req.headers['user-agent']);
  redisClient.get(key, function (err, reply) {
    if (!reply) {
      return res.status(404).send('The download link has expired');
    }

    const obj = JSON.parse(reply);
    const filePath = config.download.directory + `${obj.path}`;
    const filename = obj.filename;

    if (['Edge', 'Chrome', 'Firefox'].indexOf(ua.browser.name) > -1) {
      res.download(filePath, filename, function (err) {
        if (err) {
          logger.error('Download error');
          logger.error(err);
        }
      });
    } else {
      const mimetype = mime.lookup(filePath);
      res.setHeader('Content-type', mimetype);
      if (ua.browser.name === 'IE') {
        res.setHeader('Content-Disposition', 'attachment; filename=' + encodeURIComponent(filename));
      } else {
        res.setHeader('Content-Disposition', 'attachment; filename=' + Buffer.from(filename).toString('binary'));
      }
      const filestream = fs.createReadStream(filePath);
      filestream.pipe(res);
    }
  });
});

Takeaway: when the route should stop processing, return res.send(...) (or res.end, etc.) so the rest of the handler doesn’t continue executing.

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover