0

One of my routes always logs this "Cannot remove headers after they are sent" error. Everything functions correctly and the 200 response and sent string are all successful. My route looks like this:

    app.get('/category/:service', auth, async (req, res, next) => {
    //conditionally use middleware https://stackoverflow.com/a/21293587/2293288
    if (req.params.service == 'service1') {
        await service1(req, res, next);
    }
    if (req.params.service == 'service2') {
        await service2(req, res, next);
    }
    res.status(200).send('Thank you'); 
});

and service2:

export const service2 = async (req, res, next) => {
    try {
        //googleapi data added to db
        const example = await googleapi.create();

        next();
        return;
    } catch (err) {
        console.error('Error:', err);
        res.status(403).send('Error');
        return;
    }
};

This is the first time I have tried to structure a route like this, so I expect I messed something up within the if statements...

1 Answers1

1

As a response was sent it doesn't make sense to change any part of res; It has been sent before and there is no access to the response anymore. res.status(200).send('Thank you'); gets called on a dead response object. The error simply notices you about this situation.

1. Relay on middleware to send response

You can simply drop the last line and everything will work properly. In this case, you navigate the flow to the proper middleware and those middleware will take care of sending response. But you need to check if none of service1 or service2 is provided by the params.

    app.get('/category/:service', auth, async (req, res, next) => {
    //conditionally use middleware https://stackoverflow.com/a/21293587/2293288
    if (req.params.service == 'service1') {
        await service1(req, res, next);
    } else if (req.params.service == 'service2') {
        await service2(req, res, next);
    } else {
      res.status(403).send('You should choose either service1 or service2');
    }
});

Note: using async/await here is optional.

2. Another possible solution

Remove all sending responses from your services and handle it in the route hook:

export const service2 = async (req, res) => {
  //googleapi data added to db
  const example = await googleapi.create();
  return example;
}; // do the same for Service1


app.get('/category/:service', auth, async (req, res, next) => {
try {
    if (req.params.service == 'service1') {
        const example1 = await service1(req, res);
        next(); // You can also return response here and drop next()
        // res.status(200).send('Thank you'); 
        // res.status(200).send(example1); 
    } else if (req.params.service == 'service2') {
        const example2 = await service2(req, res);
        next();
    } else {
      res.status(403).send('You should choose either service1 or service2');
    }
} catch (e){
  console.error('Error:', err);
  res.status(403).send('Error');
}
});

Using one of these approaches will eliminate the error, because of clarifying the sending response process (providing a single source of sending response).

More for reading: 1 2

Raeisi
  • 1,647
  • 1
  • 6
  • 18
  • thank you for taking the time to write a thorough answer. I am still confused though. If no error was caught, how was a response already sent before the final res.status(200).send('Thank you') ? The only other res.send is in the error catch. – Matthew Michaud May 16 '23 at 22:34
  • You are calling the `next()` handler in your service2. So, it means, you have done your job here and the next middleware will send the response. As a gist, if you call `next()` you are no longer allowed to manipulate the `res`. – Raeisi May 17 '23 at 05:23