42

3 years ago I could do multiple res.send in express.js.
even write a setTimeout to show up a live output.

response.send('<script class="jsbin" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>');
response.send('<html><body><input id="text_box" /><button>submit</button></body></html>');
var initJs = function() {
  $('.button').click(function() {
    $.post('/input', { input: $('#text_box').val() }, function() { alert('has send');});
  });
}
response.send('<script>' + initJs + '</script>');

Now it will throw:

Error: Can't set headers after they are sent

I know nodejs and express have updated. Why can't do that now? Any other idea?


Found the solution but res.write is not in api reference http://expressjs.com/4x/api.html

vvvvv
  • 25,404
  • 19
  • 49
  • 81
emj365
  • 2,028
  • 2
  • 19
  • 24
  • There is a line on the API reference page says "The res object is an enhanced version of Node’s own response object and supports all built-in fields and methods", response.write is a built-in method. – koo Sep 04 '20 at 07:50

3 Answers3

83

Maybe you need: response.write

response.write("foo");
response.write("bar");
//...
response.end()

res.send implicitly calls res.write followed by res.end. If you call res.send multiple times, it will work the first time. However, since the first res.send call ends the response, you cannot add anything to the response.

conradkleinespel
  • 6,560
  • 10
  • 51
  • 87
yangsibai
  • 1,637
  • 12
  • 23
  • Yes, that sounds like that's probably what he was thinking of. I completely forgot about Node's `response.write`; my brain was in Express land :/ – Robert Mitchell Aug 29 '14 at 04:47
  • 4
    Cool, this's I want. But, Why it's not in api reference http://expressjs.com/4x/api.html#res.send ??? – emj365 Aug 29 '14 at 08:07
  • Maybe because we should use `res.render` or `res.send`, I use `res.write` when I was a newbie. @emj365 why not try `res.render`? – yangsibai Aug 29 '14 at 11:41
  • 3
    `respone.write` and `response.writeHead` are from the `http.ServerResponse` api [node doc](http://nodejs.org/api/http.html#http_response_write_chunk_encoding) – eephillip Jan 10 '15 at 17:58
21

response.send sends an entire HTTP response to the client, including headers and content, which is why you are unable to call it multiple times. In fact, it even ends the response, so there is no need to call response.end explicitly when using response.send.

It appears to me that you are attempting to use send like a buffer: writing to it with the intention to flush later. This is not how the method works, however; you need to build up your response in code and then make a single send call.

Unfortunately, I cannot speak to why or when this change was made, but I know that it has been like this at least since Express 3.

Robert Mitchell
  • 1,334
  • 8
  • 10
1

res.write immediately sends bytes to the client

I just wanted to make this point about res.write clearer.

It does not build up the reply and wait for res.end(). It just sends right away.

This means that the first time you call it, it will send the HTTP reply headers including the status in order to have a meaningful response. So if you want to set a status or custom header, you have to do it before that first call, much like with send().

Note that write() is not what you usually want to do in a simple web application. The browser getting the reply little by little increases the complexity of things, so you will only want to do it it if it is really needed.

Use res.locals to build the reply across middleware

This was my original use case, and res.locals fits well. I can just store data in an Array there, and then on the very last middleware join them up and do a final send to send everything at once, something like:

  async (err, req, res, next) => {
    res.locals.msg = ['Custom handler']
    next(err)
  },
  async (err, req, res, next) => {
    res.locals.msg.push('Custom handler 2')
    res.status(500).send(res.locals.msg.join('\n'))
  }
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985