24

OK, everyone knows 200 is OK and 404 is not found. But I for things like permanent vs temporary redirect, or payment required, or other more exotic HTTP error codes, it might be better to do something like:

response.status('REQUEST_ENTITY_TOO_LARGE');

Rather than just use a magic number which is generally considered bad practice. I could, of course, have 413:'REQUEST_ENTITY_TOO_LARGE' in some object, but Express already has a copy of the status code -> name mappings and I'd rather not duplicate that.

How can I specify a response status by name in Express JS?

Edit: thanks @Akshat for pointing out http.STATUS_CODES. Elaborating on his answer, since the values are themselves unique, one can run:

   var statusCodeByName = {};
   for ( var number in http.STATUS_CODES ) {
     statusCodeByName[http.STATUS_CODES[number]] = number
   }

Which allows one to:

  > statusCodeByName['Request Entity Too Large']
  '413'
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • Why is it bad to answer with a number? That number is well understood by browsers, if you try not to send it then how could the browser tell the difference between a 404 and a 500 for example? – Alberto Zaccagni Aug 19 '13 at 10:50
  • 1
    @AlbertoZaccagni Obviously we'll need to send a number over the wire, that's required by RFC2616. My interest in being able to specify the status by name (obviously still sending it by number) is for readability. – mikemaccana Aug 19 '13 at 15:34

3 Answers3

31

There's a Node module just for this purpose: http-status-codes.

https://www.npmjs.org/package/http-status-codes

Here's what the documentation says:

Installation

npm install http-status-codes

Usage

var HttpStatus = require('http-status-codes');

response.send(HttpStatus.OK);
response.send(
    HttpStatus.INTERNAL_SERVER_ERROR, 
    { error: HttpStatus.getStatusText(HttpStatus.INTERNAL_SERVER_ERROR) }
);
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
stone
  • 8,422
  • 5
  • 54
  • 66
2

HTTP response codes are not magic numbers; they are the spec. The descriptive text is just a helpful reminder, but the protocol itself relies on those status codes, and the core ones are very worth learning. Two thoughts. You can certainly create a constant at the top of your file and do this:

var REQUEST_ENTITY_TOO_LARGE = 413;
response.status(REQUEST_ENTITY_TOO_LARGE);

However, most REST APIs just implement the following responses:

200 - OK
201 - Created  # Response to successful POST or PUT
302 - Found # Temporary redirect such as to /login
303 - See Other # Redirect back to page after successful login
304 - Not Modified
400 - Bad Request
401 - Unauthorized  # Not logged in
403 - Forbidden  # Accessing another user's resource
404 - Not Found
500 - Internal Server Error

Finally, in case it's helpful, I'll share our code for rendering custom error pages:

module.exports = function(app) {

  app.use(function(req, res) {
  // curl https://localhost:4000/notfound -vk
  // curl https://localhost:4000/notfound -vkH "Accept: application/json"
    res.status(404);

    if (req.accepts('html')) {
      res.render('error/404', { title:'404: Page not found', error: '404: Page not found', url: req.url });
      return;
    }

    if (req.accepts('json')) {
      res.send({ title: '404: Page not found', error: '404: Page not found', url: req.url });
    }
  });

  app.use( function(err, req, res, next) {
    // curl https://localhost:4000/error/403 -vk
    // curl https://localhost:4000/error/403 -vkH "Accept: application/json"
    var statusCode = err.status || 500;
    var statusText = '';
    var errorDetail = (process.env.NODE_ENV === 'production') ? 'Sorry about this error' : err.stack;

    switch (statusCode) {
    case 400:
      statusText = 'Bad Request';
      break;
    case 401:
      statusText = 'Unauthorized';
      break;
    case 403:
      statusText = 'Forbidden';
      break;
    case 500:
      statusText = 'Internal Server Error';
      break;
    }

    res.status(statusCode);

    if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
      console.log(errorDetail);
    }

    if (req.accepts('html')) {
      res.render('error/500', { title: statusCode + ': ' + statusText, error: errorDetail, url: req.url });
      return;
    }

    if (req.accepts('json')) {
      res.send({ title: statusCode + ': ' + statusText, error: errorDetail, url: req.url });
    }
  });
};
Dan Kohn
  • 33,811
  • 9
  • 84
  • 100
  • 11
    A magic number is any number that appears in code without a name (generally excluding 0 and 1) - see http://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants. The 'descriptive text' is actually part of the spec - see RFC2616 at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. Additionally, rather than focusing on which status codes are popular, it's better to focus on which status codes are *correct*. – mikemaccana Aug 19 '13 at 15:38
1

This is not possible unless you are willing to change the source code yourself. Take a look at the implementation of res.send

If you provide a string as an argument it just interprets it as html and sends the response as 200.

I think the reason express uses numbers for HTTP status codes is because node itself uses numbers as object keys for http.STATUS_CODES

Akshat Jiwan Sharma
  • 15,430
  • 13
  • 50
  • 60
  • 2
    Thanks. http.STATUS_CODES is what I was looking for - since the values aren't duplicated, you could conceivably create a clone with inverted key<->values and use that. – mikemaccana Aug 19 '13 at 15:32
  • `node --print "JSON.stringify(Object.entries(http.STATUS_CODES).reduce((acc, [key, value]) => (acc[value.replace(/ /g, '_')] = key, acc), {}))" | jq '.'` – rofrol Sep 13 '22 at 20:52