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.