Swagger, Express and Content Negotiation

In a previous post, I wrote about creating the API for the The Vardyger publishing platform. In this post, we'll enhance the API by adding support for content negotiation (e.g., application/json or text/html).

The Swagger Specification

We'll start by updating the GET /posts/{id} URI, jump to the /posts/{id} section of (Vardyger/core/server/api/swagger/) swagger.yaml:

/posts/{id}:
  x-swagger-router-controller: posts
  get:
    description: Returns a post in the collection.
    operationId: findPostById
    parameters:
      - name: id
        in: path
        description: The posts ID
        required: true
        type: string
    tags:
      - Posts
    produces:
      - application/json
    responses:
      200:
    ...

All we need to do is to update the produces section as follows:

    produces:
      - application/json
      - text/html

Now, launch the API:

swagger project start

Navigate to http://localhost:10010/docs:

And, you should see two options ('application/json' and 'text/html') listed in the "Response Content Type" select control.

The Posts Controller

We also need to update the findPostById operation, in the (Vardyger/core/server/api/controllers/) posts.js controller:

function findPostById(req, res) {
  var id = req.swagger.params.id.value,
      view = 'post',
      response;

  Post.findById(id).exec(function(error, model) {

    if (! error) {
      if (null === model || undefined === model) { 
        returnError(res, status.NOT_FOUND); 
      }

      response = formatResponse(model);

      res.format({
        html: function() {
          res.type(TEXT_HTML);
          res.render(view, response);
        },
        json: function() {
          res.type(APPLICATION_JSON);
          res.status(status.OK).send(JSON.stringify(response));
        },
        default: function() { 
          returnError(res, status.NOT_ACCEPTABLE); 
        }
      })
    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}  

The heavy lifting is handled by Express's res.format() which performs content-negotiation on the Accept HTTP header of the request object.

Try it, from the command line:

curl http://localhost:10010/v1/posts/{id} --header "Accept: text/html"

Or, from your browser:

Note: I also needed to set validateResponse: false in the config object passed to the Swagger Express middleware (swagger-express-mw).

References: