Restify is a simple Node back end framework.
In this article, we’ll look at how to handle content negotiation and errors with Restify.
Content Negotiation And Formatting
Restify will determine the content-type to respond with by from highest to lowest priority.
It’ll use the res.contentType if it’s present.
The Content-Type response header if it’s set.
application/json will be returned if the body is an object and not a Buffer instance.
Otherwise, it negotiates the content-type by matching the available formatters with the requests’s accept header.
If a content-type can’t be determined then Restify will respond with an error.
If a content-type can be negotiated, then it determines what formatter to use to format the response’s content.
If there’s no formatter matching the content-type can be found, then Restify will override the response’s content-type to 'application/octet-stream' and then error out if no formnatter if found for that content-type .
The default behavior can be changed by passing the strictFormatters: false property when creating the Restify server instance.
If no formatter is found for the content-type , then the response is flushed without applying any formatter.
Restify ships with application/json , text/plain , and application/octet-stream formatters.
We can add additional formatters to Restify by passing a hash of content type and parser when we create the server.
For example, we can write:
var restify = require('restify');
const util = require('util');
function respond(req, res, next) {
res.send('hello');
next();
}
var server = restify.createServer({
formatters: {
['application/foo'](req, res, body) {
if (body instanceof Error)
return body.stack;
if (Buffer.isBuffer(body))
return body.toString('base64');
return util.inspect(body);
}
}
});
server.get('/hello', respond);
server.head('/hello', respond);
server.listen(8080);
We have the formatters object for the application/foo content type.
Then we return a base64 string if the body is a buffer.
We can also add a q-value to our formatter definition to change the priority of the formatter:
var restify = require('restify');
const util = require('util');
function respond(req, res, next) {
res.send('hello');
next();
}
var server = restify.createServer({
formatters: {
['application/foo q=0.9'](req, res, body) {
if (body instanceof Error)
return body.stack;
if (Buffer.isBuffer(body))
return body.toString('base64');
return util.inspect(body);
}
}
});
server.get('/hello', respond);
server.head('/hello', respond);
server.listen(8080);
Restify ships with default formatters.
It can be overridden when passing formatter options with createServer :
var restify = require('restify');
function respond(req, res, next) {
const body = 'hello world';
res.writeHead(200, {
'Content-Length': Buffer.byteLength(body),
'Content-Type': 'text/plain'
});
res.write(body);
res.end();
}
var server = restify.createServer();
server.get('/hello', respond);
server.head('/hello', respond);
server.listen(8080);
We called the writeHead method with an object with the content-type options we want to return in the header.
Error Handling
We can handle error conditions by passing an error object with the next function.
For example, we can write:
var restify = require('restify');
var errors = require('restify-errors');
var server = restify.createServer();
server.get('/hello/:foo', function(req, res, next) {
var err = new errors.NotFoundError('not found');
return next(err);
});
server.on('NotFound', function (req, res, err, cb) {
return cb();
});
server.listen(8080);
We have the NotFound handler to do logging or collect data.
The NotFoundError constructor is in the restify-errors module.
We shouldn’t call res.send in the NotFound handler since it’s in a different error context.
Conclusion
We can format the our data and do content negotiating the same way.
Also, we create error objects with the restify-errors module.