SIGN IN SIGN UP
restify / node-restify UNCLAIMED

The future of Node.js REST development

0 0 15 JavaScript
---
title: Plugins API
permalink: /docs/plugins-api/
---
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [Usage][1]
- [server.pre() plugins][2]
- [context][3]
- [dedupeSlashes][4]
- [pause][5]
- [sanitizePath][6]
- [reqIdHeaders][7]
- [strictQueryParams][8]
- [userAgentConnection][9]
- [server.use() plugins][10]
- [acceptParser][11]
- [authorizationParser][12]
- [dateParser][13]
- [queryParser][14]
- [jsonp][15]
- [bodyParser][16]
- [requestLogger][17]
- [gzipResponse][18]
- [serveStatic][19]
- [serveStaticFiles][20]
- [throttle][21]
- [requestExpiry][22]
- [Using an external storage mechanism for key/bucket mappings.][23]
- [inflightRequestThrottle][24]
- [cpuUsageThrottle][25]
- [conditionalHandler][26]
- [conditionalRequest][27]
- [auditLogger][28]
- [metrics][29]
- [Types][30]
- [metrics~callback][31]
- [req.set][32]
- [req.get][33]
- [req.getAll][34]
## Usage
Restify comes bundled with a selection of useful plugins. These are accessible
off of `restify.plugins` and `restify.pre`.
```js
var server = restify.createServer();
server.use(restify.plugins.acceptParser(server.acceptable));
server.use(restify.plugins.authorizationParser());
server.use(restify.plugins.dateParser());
server.use(restify.plugins.queryParser());
server.use(restify.plugins.jsonp());
server.use(restify.plugins.gzipResponse());
server.use(restify.plugins.bodyParser());
server.use(restify.plugins.requestExpiry());
server.use(restify.plugins.throttle({
burst: 100,
rate: 50,
ip: true,
overrides: {
'192.168.1.1': {
rate: 0, // unlimited
burst: 0
}
}
}));
server.use(restify.plugins.conditionalRequest());
```
## server.pre() plugins
This module includes various pre plugins, which are intended to be used prior
to routing of the URL. To use a plugin before routing, use the `server.pre()`
method.
### context
This plugin creates `req.set(key, val)` and `req.get(key)` methods for
setting and retrieving request specific data.
**Examples**
```javascript
server.pre(restify.plugins.pre.context());
server.get('/', [
function(req, res, next) {
req.set(myMessage, 'hello world');
return next();
},
function two(req, res, next) {
res.send(req.get(myMessage)); // => sends 'hello world'
return next();
}
]);
```
Returns **[Function][35]** Handler
### dedupeSlashes
This plugin deduplicates extra slashes found in the URL. This can help with
malformed URLs that might otherwise get misrouted.
**Examples**
```javascript
server.pre(restify.plugins.pre.dedupeSlashes());
server.get('/hello/:one', function(req, res, next) {
res.send(200);
return next();
});
// the server will now convert requests to /hello//jake => /hello/jake
```
Returns **[Function][35]** Handler
### pause
This pre handler fixes issues with node hanging when an `asyncHandler` is
used prior to `bodyParser`.
[https://github.com/restify/node-restify/issues/287][36]
[https://github.com/restify/node-restify/issues/409][37]
[https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips][38]
Returns **[Function][35]** Handler
### sanitizePath
Cleans up sloppy URLs on the request object,
like `/foo////bar///` to `/foo/bar`.
Returns **[Function][35]** Handler
### reqIdHeaders
This plugin pulls the value from an incoming request header and uses it
as the value of the request id. Subsequent calls to `req.id()`
will return the header values.
**Parameters**
- `opts` **[Object][39]** an options object
- `opts.headers` **[Array][40]&lt;[String][41]>** array of headers from where to pull existing
request id headers. Lookup precedence
is left to right (lowest index first)
Returns **[Function][35]** Handler
### strictQueryParams
Prevents `req.urls` non-strict key-value query params
The Request-URI is transmitted in the format specified in section 3.2.1.
If the Request-URI is encoded using the "% HEX HEX" encoding [42],
the origin server MUST decode the Request-URI
in order to properly interpret the request.
Servers SHOULD respond to invalid Request-URIs
with an appropriate status code.
part of Hypertext Transfer Protocol -- HTTP/1.1 | 5.1.2 Request-URI
RFC 2616 Fielding, et al.
**Parameters**
- `options` **[Object][39]?** an options object
- `options.message` **[String][41]?** a custom error message
default value:
"Url query params does not meet strict format"
Returns **[Function][35]** Handler
### userAgentConnection
This basically exists for `curl`. `curl` on `HEAD` requests usually
just sits there and hangs, unless you explicitly set
Connection:close. And in general, you probably want to set
Connection: close to curl anyway.
Also, because curl spits out an annoying message to stderr about
remaining bytes if content-length is set, this plugin also drops
the `content-length` header (some user agents handle it and want it,
curl does not).
To be slightly more generic, the options block takes a user
agent regexp, however.
**Parameters**
- `options` **[Object][39]?** an options object
- `options.userAgentRegExp` **[RegExp][42]** matching any
user-agents applicable (optional, default `/^curl.+/`)
Returns **[Function][35]** Handler
## server.use() plugins
### acceptParser
Parses the `Accept` header, and ensures that the server can respond to what
the client asked for. In almost all cases passing in `server.acceptable` is
all that's required, as that's an array of content types the server knows
how to respond to (with the formatters you've registered). If the request is
for a non-handled type, this plugin will return a `NotAcceptableError` (406).
Note you can get the set of types allowed from a restify server by doing
`server.acceptable`.
**Parameters**
- `accepts` **[Array][40]&lt;[String][41]>** array of accept types.
**Examples**
```javascript
server.use(restify.plugins.acceptParser(server.acceptable));
```
- Throws **NotAcceptableError**
Returns **[Function][35]** restify handler.
### authorizationParser
Parses out the `Authorization` header as best restify can.
Currently only HTTP Basic Auth and
[HTTP Signature][43]
schemes are supported.
**Parameters**
- `options` **[Object][39]?** an optional options object that is
passed to http-signature
**Examples**
Subsequent handlers will see `req.authorization`, which looks like above.
`req.username` will also be set, and defaults to 'anonymous'. If the scheme
is unrecognized, the only thing available in `req.authorization` will be
`scheme` and `credentials` - it will be up to you to parse out the rest.
```javascript
{
scheme: "<Basic|Signature|...>",
credentials: "<Undecoded value of header>",
basic: {
username: $user
password: $password
}
}
```
- Throws **InvalidArgumentError**
Returns **[Function][35]** Handler
### dateParser
Parses out the HTTP Date header (if present) and checks for clock skew.
If the header is invalid, a `InvalidHeaderError` (`400`) is returned.
If the clock skew exceeds the specified value,
a `RequestExpiredError` (`400`) is returned.
Where expired means the request originated at a time
before (`$now - $clockSkew`).
The default clockSkew allowance is 5m (thanks
Kerberos!)
**Parameters**
- `clockSkew` **[Number][44]** allowed clock skew in seconds. (optional, default `300`)
**Examples**
```javascript
// Allows clock skew of 1m
server.use(restify.plugins.dateParser(60));
```
- Throws **RequestExpiredError**
- Throws **InvalidHeaderError**
Returns **[Function][35]** restify handler.
### queryParser
Parses the HTTP query string (i.e., `/foo?id=bar&name=mark`).
If you use this, the parsed content will always be available in `req.query`,
additionally params are merged into `req.params`.
You can disable by passing in `mapParams: false` in the options object.
Many options correspond directly to option defined for the underlying
[`qs.parse`][45].
**Parameters**
- `options` **[Object][39]?** an options object
- `options.mapParams` **[Object][39]** disable passing (optional, default `true`)
- `options.mapParams` **[Boolean][46]** Copies parsed query parameters
into`req.params`. (optional, default `false`)
- `options.overrideParams` **[Boolean][46]** Only applies when if
mapParams true.
When true, will stomp on req.params field when existing value is found. (optional, default `false`)
- `options.allowDots` **[Boolean][46]** Transform `?foo.bar=baz` to a
nested object: `{foo: {bar: 'baz'}}`. (optional, default `false`)
- `options.arrayLimit` **[Number][44]** Only transform `?a[$index]=b`
to an array if `$index` is less than `arrayLimit`. (optional, default `20`)
- `options.depth` **[Number][44]** The depth limit for parsing
nested objects, e.g. `?a[b][c][d][e][f][g][h][i]=j`. (optional, default `5`)
- `options.parameterLimit` **[Number][44]** Maximum number of query
params parsed. Additional params are silently dropped. (optional, default `1000`)
- `options.parseArrays` **[Boolean][46]** Whether to parse
`?a[]=b&a[1]=c` to an array, e.g. `{a: ['b', 'c']}`. (optional, default `true`)
- `options.plainObjects` **[Boolean][46]** Whether `req.query` is a
"plain" object -- does not inherit from `Object`.
This can be used to allow query params whose names collide with Object
methods, e.g. `?hasOwnProperty=blah`. (optional, default `false`)
- `options.strictNullHandling` **[Boolean][46]** If true, `?a&b=`
results in `{a: null, b: ''}`. Otherwise, `{a: '', b: ''}`. (optional, default `false`)
**Examples**
```javascript
server.use(restify.plugins.queryParser({ mapParams: false }));
```
Returns **[Function][35]** Handler
### jsonp
Parses the jsonp callback out of the request.
Supports checking the query string for `callback` or `jsonp` and ensuring
that the content-type is appropriately set if JSONP params are in place.
There is also a default `application/javascript` formatter to handle this.
You _should_ set the `queryParser` plugin to run before this, but if you
don't this plugin will still parse the query string properly.
**Examples**
```javascript
var server = restify.createServer();
server.use(restify.plugins.jsonp());
```
Returns **[Function][35]** Handler
### bodyParser
Blocks your chain on reading and parsing the HTTP request body. Switches on
`Content-Type` and does the appropriate logic. `application/json`,
`application/x-www-form-urlencoded` and `multipart/form-data` are currently
supported.
Parses `POST` bodies to `req.body`. automatically uses one of the following
parsers based on content type:
- `urlEncodedBodyParser(options)` - parses url encoded form bodies
- `jsonBodyParser(options)` - parses JSON POST bodies
- `multipartBodyParser(options)` - parses multipart form bodies
All bodyParsers support the following options:
- `options.mapParams` - default false. copies parsed post body values onto
req.params
- `options.overrideParams` - default false. only applies when if
mapParams true. when true, will stomp on req.params value when
existing value is found.
**Parameters**
- `options` **[Object][39]?** an option object
- `options.maxBodySize` **[Number][44]?** The maximum size in bytes allowed in
the HTTP body. Useful for limiting clients from hogging server memory.
- `options.mapParams` **[Boolean][46]?** if `req.params` should be filled with
parsed parameters from HTTP body.
- `options.mapFiles` **[Boolean][46]?** if `req.params` should be filled with
the contents of files sent through a multipart request.
[formidable][47] is used internally
for parsing, and a file is denoted as a multipart part with the `filename`
option set in its `Content-Disposition`. This will only be performed if
`mapParams` is true.
- `options.overrideParams` **[Boolean][46]?** if an entry in `req.params`
should be overwritten by the value in the body if the names are the same.
For instance, if you have the route `/:someval`,
and someone posts an `x-www-form-urlencoded`
Content-Type with the body `someval=happy` to `/sad`, the value will be
`happy` if `overrideParams` is `true`, `sad` otherwise.
- `options.multipartHandler` **[Function][35]?** a callback to handle any
multipart part which is not a file.
If this is omitted, the default handler is invoked which may
or may not map the parts into `req.params`, depending on
the `mapParams`-option.
- `options.multipartFileHandler` **[Function][35]?** a callback to handle any
multipart file.
It will be a file if the part has a `Content-Disposition` with the
`filename` parameter set. This typically happens when a browser sends a
form and there is a parameter similar to `<input type="file" />`.
If this is not provided, the default behaviour is to map the contents
into `req.params`.
- `options.keepExtensions` **[Boolean][46]?** if you want the uploaded
files to include the extensions of the original files
(multipart uploads only).
Does nothing if `multipartFileHandler` is defined.
- `options.uploadDir` **[String][41]?** Where uploaded files are
intermediately stored during transfer before the contents is mapped
into `req.params`.
Does nothing if `multipartFileHandler` is defined.
- `options.multiples` **[Boolean][46]?** if you want to support html5 multiple
attribute in upload fields.
- `options.hash` **[String][41]?** If you want checksums calculated for
incoming files, set this to either `sha1` or `md5`.
- `options.rejectUnknown` **[Boolean][46]?** Set to `true` if you want to end
the request with a `UnsupportedMediaTypeError` when none of
the supported content types was given.
- `options.requestBodyOnGet` **[Boolean][46]** Parse body of a GET
request. (optional, default `false`)
- `options.reviver` **[Function][35]?** `jsonParser` only. If a function,
this prescribes how the value originally produced by parsing is transformed,
before being returned. For more information check out
`JSON.parse(text[, reviver])`.
- `options.maxFieldsSize` **[Number][44]** `multipartParser`
only.
Limits the amount of memory all fields together (except files)
can allocate in bytes.
The default size is `2 * 1024 * 1024` bytes _(2MB)_. (optional, default `2*1024*1024`)
**Examples**
```javascript
server.use(restify.plugins.bodyParser({
maxBodySize: 0,
mapParams: true,
mapFiles: false,
overrideParams: false,
multipartHandler: function(part) {
part.on('data', function(data) {
// do something with the multipart data
});
},
multipartFileHandler: function(part) {
part.on('data', function(data) {
// do something with the multipart file data
});
},
keepExtensions: false,
uploadDir: os.tmpdir(),
multiples: true,
hash: 'sha1',
rejectUnknown: true,
requestBodyOnGet: false,
reviver: undefined,
maxFieldsSize: 2 * 1024 * 1024
}));
```
- Throws **UnsupportedMediaTypeError**
Returns **[Function][35]** Handler
### requestLogger
Sets up a child [bunyan][48] logger with
the current request id filled in, along with any other parameters you define.
You can pass in no options to this, in which case only the request id will be
appended, and no serializers appended (this is also the most performant); the
logger created at server creation time will be used as the parent logger.
This logger can be used normally, with [req.log][49].
This plugin does _not_ log each individual request. Use the Audit Logging
plugin or a custom middleware for that use.
**Parameters**
- `options` **[Object][39]?** an options object
- `options.headers` **[Array][40]?** A list of headers to transfer from
the request to top level props on the log.
**Examples**
```javascript
server.use(restify.plugins.requestLogger({
properties: {
foo: 'bar'
},
serializers: {...}
}));
```
Returns **[Function][35]** Handler
### gzipResponse
If the client sends an `accept-encoding: gzip` header (or one with an
appropriate q-val), then the server will automatically gzip all
response data.
Note that only `gzip` is supported, as this is most widely supported by
clients in the wild.
This plugin will overwrite some of the internal streams, so any
calls to `res.send`, `res.write`, etc., will be compressed. A side effect is
that the `content-length` header cannot be known, and so
`transfer-encoding: chunked` will _always_ be set when this is in effect.
This plugin has no impact if the client does not send
`accept-encoding: gzip`.
[https://github.com/restify/node-restify/issues/284][50]
**Parameters**
- `opts` **[Object][39]?** an options object, see: zlib.createGzip
**Examples**
```javascript
server.use(restify.plugins.gzipResponse());
```
Returns **[Function][35]** Handler
### serveStatic
Serves static files.
**Parameters**
- `options` **[Object][39]** an options object
**Examples**
The serveStatic module is different than most of the other plugins, in that
it is expected that you are going to map it to a route, as below:
```javascript
2018-03-20 11:10:28 -07:00
server.get('/docs/current/*', restify.plugins.serveStatic({
directory: './documentation/v1',
default: 'index.html'
}));
```
The above `route` and `directory` combination will serve a file located in
`./documentation/v1/docs/current/index.html` when you attempt to hit
`http://localhost:8080/docs/current/`. If you want the serveStatic module to
serve files directly from the `/documentation/v1` directory
(and not append the request path `/docs/current/`),
you can set the `appendRequestPath` option to `false`, and the served file
would be `./documentation/v1/index.html`, in the previous example.
The plugin will enforce that all files under `directory` are served.
The `directory` served is relative to the process working directory.
You can also provide a `default` parameter such as index.html for any
directory that lacks a direct file match.
You can specify additional restrictions by passing in a `match` parameter,
which is just a `RegExp` to check against the requested file name.
Additionally, you may set the `charSet` parameter, which will append a
character set to the content-type detected by the plugin.
For example, `charSet: 'utf-8'` will result in HTML being served with a
`Content-Type` of `text/html; charset=utf-8`.
Lastly, you can pass in a `maxAge` numeric, which will set the
`Cache-Control` header. Default is `3600` (1 hour).
An additional option for serving a static file is to pass `file` in to the
serveStatic method as an option. The following will serve index.html from
the documentation/v1/ directory anytime a client requests `/home/`.
```javascript
2018-03-20 11:10:28 -07:00
server.get('/home/*', restify.plugins.serveStatic({
directory: './documentation/v1',
file: 'index.html'
}));
// or
server.get('/home/([a-z]+[.]html)', restify.plugins.serveStatic({
directory: './documentation/v1',
file: 'index.html'
}));
```
- Throws **MethodNotAllowedError** \|
- Throws **NotAuthorizedError**
- Throws **ResourceNotFoundError**
Returns **[Function][35]** Handler
### serveStaticFiles
Serves static files, with API similar to expressjs
**Parameters**
- `directory` **[String][41]** the directory to serve files from
- `opts` **[Object][39]** an options object, which is optional
- `opts.maxAge` **[Number][44]** specify max age in millisecs (optional, default `0`)
- `opts.etag` **[Boolean][46]** enable/disable etag, default = true (optional, default `true`)
- `opts.setHeaders` **[Function][35]?** set custom headers for the Files
(synchronously), The function is called as `fn(res, path, stat)`,
where the arguments are:
`res` the response object
`path` the file path that is being sent
`stat` the stat object of the file that is being sent
**Examples**
The serveStaticFiles plugin allows you to map a GET route to a
directory on the disk
```javascript
server.get('/public/*', // don't forget the `/*`
restify.plugins.serveStaticFiles('./documentation/v1')
);
```
The GET `route` and `directory` combination will serve a file
located in `./documentation/v1/index.html` when you attempt to hit
`http://localhost:8080/public/index.html`
The plugin uses [send][51] under the hood
which is also used by `expressjs` to serve static files. Most of the options
that work with `send` will work with this plugin.
The default file the plugin looks for is `index.html`
```javascript
server.get('/public/*',
restify.plugins.serveStaticFiles('./documentation/v1', {
maxAge: 3600000, // this is in millisecs
etag: false,
setHeaders: function setCustomHeaders(response, requestedPath, stat) {
response.setHeader('restify-plugin-x', 'awesome');
}
})
);
```
- Throws **MethodNotAllowedError**
- Throws **NotAuthorizedError**
- Throws **ResourceNotFoundError**
Returns **[Function][35]** Handler
### throttle
Creates an API rate limiter that can be plugged into the standard
restify request handling pipeline.
`restify` ships with a fairly comprehensive implementation of
[Token bucket][52], with the ability
to throttle on IP (or x-forwarded-for) and username (from `req.username`).
You define "global" request rate and burst rate, and you can define
overrides for specific keys.
Note that you can always place this on per-URL routes to enable
different request rates to different resources (if for example, one route,
like `/my/slow/database` is much easier to overwhlem
than `/my/fast/memcache`).
If a client has consumed all of their available rate/burst, an HTTP response
code of `429`
[Too Many Requests][53]
is returned.
This throttle gives you three options on which to throttle:
username, IP address and 'X-Forwarded-For'. IP/XFF is a /32 match,
so keep that in mind if using it. Username takes the user specified
on req.username (which gets automagically set for supported Authorization
types; otherwise set it yourself with a filter that runs before this).
In both cases, you can set a `burst` and a `rate` (in requests/seconds),
as an integer/float. Those really translate to the `TokenBucket`
algorithm, so read up on that (or see the comments above...).
In either case, the top level options burst/rate set a blanket throttling
rate, and then you can pass in an `overrides` object with rates for
specific users/IPs. You should use overrides sparingly, as we make a new
TokenBucket to track each.
On the `options` object ip and username are treated as an XOR.
**Parameters**
- `options` **[Object][39]** required options with:
- `options.burst` **[Number][44]** burst
- `options.rate` **[Number][44]** rate
- `options.ip` **[Boolean][46]?** ip
- `options.username` **[Boolean][46]?** username
- `options.xff` **[Boolean][46]?** xff
- `options.setHeaders` **[Boolean][46]** Set response headers for rate,
limit (burst) and remaining. (optional, default `false`)
- `options.overrides` **[Object][39]?** overrides
- `options.tokensTable` **[Object][39]** a storage engine this plugin will
use to store throttling keys -> bucket mappings.
If you don't specify this, the default is to
use an in-memory O(1) LRU, with 10k distinct
keys. Any implementation just needs to support
put/get.
- `options.maxKeys` **[Number][44]** If using the default
implementation, you can specify how large you
want the table to be. (optional, default `10000`)
**Examples**
An example options object with overrides:
```javascript
{
burst: 10, // Max 10 concurrent requests (if tokens)
rate: 0.5, // Steady state: 1 request / 2 seconds
ip: true, // throttle per IP
overrides: {
'192.168.1.1': {
burst: 0,
rate: 0 // unlimited
}
}
```
- Throws **TooManyRequestsError**
Returns **[Function][35]** Handler
### requestExpiry
Request Expiry can be used to throttle requests that have already exceeded
their client timeouts. Requests can be sent with a configurable client
timeout header, e.g. 'x-request-expiry-time', which gives in absolute ms
since epoch, when this request will be timed out by the client.
This plugin will throttle all incoming requests via a 504 where
'x-request-expiry-time' less than Date.now() -- since these incoming requests
have already been timed out by the client. This prevents the server from
processing unnecessary requests.
Request expiry will use headers to tell if the incoming request has expired.
There are two options for this plugin:
1\. Absolute Time
_ Time in Milliseconds since Epoch when this request should be
considered expired
2\. Timeout
_ The request start time is supplied
_ A timeout, in milliseconds, is given
_ The timeout is added to the request start time to arrive at the
absolute time in which the request is considered expired
#### Using an external storage mechanism for key/bucket mappings.
By default, the restify throttling plugin uses an in-memory LRU to store
mappings between throttling keys (i.e., IP address) to the actual bucket that
key is consuming. If this suits you, you can tune the maximum number of keys
to store in memory with `options.maxKeys`; the default is 10000.
In some circumstances, you want to offload this into a shared system, such as
Redis, if you have a fleet of API servers and you're not getting steady
and/or uniform request distribution. To enable this, you can pass in
`options.tokensTable`, which is simply any Object that supports `put` and
`get` with a `String` key, and an `Object` value.
**Parameters**
- `opts` **[Object][39]** an options object
- `opts.absoluteHeader` **[String][41]?** The header key to be used for
the expiry time of each request.
- `opts.startHeader` **[String][41]** The header key for the start time
of the request.
- `opts.timeoutHeader` **[String][41]** The header key for the time in
milliseconds that should ellapse before
the request is considered expired.
**Examples**
The only option provided is `header` which is the request header used
to specify the client timeout.
```javascript
server.use(restify.plugins.requestExpiry({
header: 'x-request-expiry-time'
});
```
Returns **[Function][35]** Handler
### inflightRequestThrottle
The `inflightRequestThrottle` module allows you to specify an upper limit to
the maximum number of inflight requests your server is able to handle. This
is a simple heuristic for protecting against event loop contention between
requests causing unacceptable latencies.
The custom error is optional, and allows you to specify your own response
and status code when rejecting incoming requests due to too many inflight
requests. It defaults to `503 ServiceUnavailableError`.
This plugin should be registered as early as possibly in the middleware stack
using `pre` to avoid performing unnecessary work.
**Parameters**
- `opts` **[Object][39]** configure this plugin
- `opts.limit` **[Number][44]** maximum number of inflight requests the server
will handle before returning an error
- `opts.err` **[Error][54]** A restify error used as a response when the
inflight request limit is exceeded
- `opts.server` **[Function][35]** the instance of the restify server this
plugin will throttle.
**Examples**
```javascript
var errors = require('restify-errors');
var restify = require('restify');
var server = restify.createServer();
const options = { limit: 600, server: server };
options.res = new errors.InternalServerError();
server.pre(restify.plugins.inflightRequestThrottle(options));
```
Returns **[Function][35]** middleware to be registered on server.pre
### cpuUsageThrottle
cpuUsageThrottle is a middleware that rejects a variable number of requests
(between 0% and 100%) based on a historical view of CPU utilization of a
Node.js process. Essentially, this plugin allows you to define what
constitutes a saturated Node.js process via CPU utilization and it will
handle dropping a % of requests based on that definiton. This is useful when
you would like to keep CPU bound tasks from piling up causing an increased
per-request latency.
The algorithm asks you for a maximum CPU utilization rate, which it uses to
determine at what point it should be rejecting 100% of traffic. For a normal
Node.js service, this is 1 since Node is single threaded. It uses this,
paired with a limit that you provide to determine the total % of traffic it
should be rejecting. For example, if you specify a limit of .5 and a max of
1, and the current EWMA (next paragraph) value reads .75, this plugin will
reject approximately 50% of all requests.
When looking at the process' CPU usage, this algorithm will take a load
average over a user specified interval. example, if given an interval of
250ms, this plugin will attempt to record the average CPU utilization over
250ms intervals. Due to contention for resources, the duration of each
average may be wider or narrower than 250ms. To compensate for this, we use
an exponentially weighted moving average. The EWMA algorithm is provided by
the ewma module. The parameter for configuring the EWMA is halfLife. This
value controls how quickly each load average measurment decays to half it's
value when being represented in the current average. For example, if you
have an interval of 250, and a halfLife of 250, you will take the previous
ewma value multiplied by 0.5 and add it to the new CPU utilization average
measurement multiplied by 0.5. The previous value and the new measurement
would each represent 50% of the new value. A good way of thinking about the
halfLife is in terms of how responsive this plugin will be to spikes in CPU
utilization. The higher the halfLife, the longer CPU utilization will have
to remain above your defined limit before this plugin begins rejecting
requests and, converserly, the longer it will have to drop below your limit
before the plugin begins accepting requests again. This is a knob you will
want to with play when trying to determine the ideal value for your use
case.
For a better understanding of the EWMA algorithn, refer to the documentation
for the ewma module.
**Parameters**
- `opts` **[Object][39]** Configure this plugin.
- `opts.limit` **[Number][44]?** The point at which restify will begin
rejecting a % of all requests at the front door.
This value is a percentage.
For example 0.8 === 80% average CPU utilization. Defaults to 0.75.
- `opts.max` **[Number][44]?** The point at which restify will reject 100% of
all requests at the front door. This is used in conjunction with limit to
determine what % of traffic restify needs to reject when attempting to
bring the average load back to the user requested values. Since Node.js is
single threaded, the default for this is 1. In some rare cases, a Node.js
process can exceed 100% CPU usage and you will want to update this value.
- `opts.interval` **[Number][44]?** How frequently we calculate the average CPU
utilization. When we calculate an average CPU utilization, we calculate it
over this interval, and this drives whether or not we should be shedding
load. This can be thought of as a "resolution" where the lower this value,
the higher the resolution our load average will be and the more frequently
we will recalculate the % of traffic we should be shedding. This check
is rather lightweight, while the default is 250ms, you should be able to
decrease this value without seeing a significant impact to performance.
- `opts.halfLife` **[Number][44]?** When we sample the CPU usage on an
interval, we create a series of data points.
We take these points and calculate a
moving average. The halfLife indicates how quickly a point "decays" to
half it's value in the moving average. The lower the halfLife, the more
impact newer data points have on the average. If you want to be extremely
responsive to spikes in CPU usage, set this to a lower value. If you want
your process to put more emphasis on recent historical CPU usage when
determininng whether it should shed load, set this to a higher value. The
unit is in ms. Defaults to 250.
**Examples**
```javascript
var restify = require('restify');
var server = restify.createServer();
const options = {
limit: .75,
max: 1,
interval: 250,
halfLife: 500,
}
server.pre(restify.plugins.cpuUsageThrottle(options));
```
You can also update the plugin during runtime using the `.update()` function.
This function accepts the same `opts` object as a constructor.
```javascript
var plugin = restify.plugins.cpuUsageThrottle(options);
server.pre(plugin);
plugin.update({ limit: .4, halfLife: 5000 });
```
Returns **[Function][35]** middleware to be registered on server.pre
2018-03-20 11:10:28 -07:00
### conditionalHandler
Runs first handler that matches to the condition
**Parameters**
- `candidates` **([Object][39] \| [Array][40]&lt;[Object][39]>)** candidates
- `candidates.handler` **([Function][35] \| [Array][40]&lt;[Function][35]>)** handler(s)
- `candidates.version` **([String][41] \| [Array][40]&lt;[String][41]>)?** '1.1.0', ['1.1.0', '1.2.0']
- `candidates.contentType` **[String][41]?** accepted content type, '\*\\/json'
2018-03-20 11:10:28 -07:00
**Examples**
```javascript
server.use(restify.plugins.conditionalHandler({
contentType: 'application/json',
version: '1.0.0',
handler: function (req, res, next) {
next();
})
});
server.get('/hello/:name', restify.plugins.conditionalHandler([
{
version: '1.0.0',
handler: function(req, res, next) { res.send('1.x'); }
},
{
version: ['1.5.0', '2.0.0'],
handler: function(req, res, next) { res.send('1.5.x, 2.x'); }
},
{
version: '3.0.0',
contentType: ['text/html', 'text/html']
handler: function(req, res, next) { res.send('3.x, text'); }
},
{
version: '3.0.0',
contentType: 'application/json'
handler: function(req, res, next) { res.send('3.x, json'); }
},
// Array of handlers
{
version: '4.0.0',
handler: [
function(req, res, next) { next(); },
function(req, res, next) { next(); },
function(req, res, next) { res.send('4.x') }
]
},
]);
// 'accept-version': '^1.1.0' => 1.5.x, 2.x'
// 'accept-version': '3.x', accept: 'application/json' => '3.x, json'
```
- Throws **InvalidVersionError**
- Throws **UnsupportedMediaTypeError**
Returns **[Function][35]** Handler
2018-03-20 11:10:28 -07:00
### conditionalRequest
Returns a set of plugins that will compare an already set `ETag` header with
the client's `If-Match` and `If-None-Match` header, and an already set
Last-Modified header with the client's `If-Modified-Since` and
`If-Unmodified-Since` header.
You can use this handler to let clients do nice HTTP semantics with the
"match" headers. Specifically, with this plugin in place, you would set
`res.etag=$yourhashhere`, and then this plugin will do one of:
- return `304` (Not Modified) [and stop the handler chain]
- return `412` (Precondition Failed) [and stop the handler chain]
- Allow the request to go through the handler chain.
The specific headers this plugin looks at are:
- `Last-Modified`
- `If-Match`
- `If-None-Match`
- `If-Modified-Since`
- `If-Unmodified-Since`
**Examples**
```javascript
server.use(restify.plugins.conditionalRequest());
```
```javascript
server.use(function setETag(req, res, next) {
res.header('ETag', 'myETag');
res.header('Last-Modified', new Date());
});
server.use(restify.plugins.conditionalRequest());
server.get('/hello/:name', function(req, res, next) {
res.send('hello ' + req.params.name);
});
```
- Throws **BadRequestError**
- Throws **PreconditionFailedError**
Returns **[Array][40]&lt;[Function][35]>** Handlers
### auditLogger
**Parameters**
- `opts` **[Object][39]** The options object.
- `opts.log` **[Object][39]** The logger.
- `opts.event` **[String][41]** The event from the server which initiates the
log, one of 'pre', 'routed', or 'after'
- `opts.context` **[Function][35]?** The optional context function of signature
f(req, res, route, err). Invoked each time an audit log is generated. This
function can return an object that customizes the format of anything off the
req, res, route, and err objects. The output of this function will be
available on the `context` key in the audit object.
- `opts.server` **[Object][39]?** The restify server, used to emit
the audit log object programmatically
- `opts.printLog` **[boolean][46]** Whether to print the log
via the logger. (optional, default `true`)
- `opts.serializers` **[Object][39]?** Override the default logger serializers
for err, req and res
**Examples**
Audit logging is a special plugin, as you don't use it with `.use()`
but with the `after` event:
```javascript
server.on('after', restify.plugins.auditLogger({
log: bunyan.createLogger({
name: 'audit',
stream: process.stdout
}),
event: 'after',
server: SERVER,
logMetrics : logBuffer,
printLog : true
}));
```
You pass in the auditor a bunyan logger, optionally server object,
Ringbuffer and a flag printLog indicate if log needs to be print out at info
level or not. By default, without specify printLog flag, it will write out
record lookling like this:
```javascript
{
"name": "audit",
"hostname": "your.host.name",
"audit": true,
"remoteAddress": "127.0.0.1",
"remotePort": 57692,
"req_id": "ed634c3e-1af0-40e4-ad1e-68c2fb67c8e1",
"req": {
"method": "GET",
"url": "/foo",
"headers": {
"authorization": "Basic YWRtaW46am95cGFzczEyMw==",
"user-agent": "curl/7.19.7 (universal-apple-darwin10.0)
libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3",
"host": "localhost:8080",
"accept": "application/json"
},
"httpVersion": "1.1",
"query": {
"foo": "bar"
},
"trailers": {},
"version": "*",
"timers": {
"bunyan": 52,
"saveAction": 8,
"reqResTracker": 213,
"addContext": 8,
"addModels": 4,
"resNamespaces": 5,
"parseQueryString": 11,
"instanceHeaders": 20,
"xForwardedProto": 7,
"httpsRedirector": 14,
"readBody": 21,
"parseBody": 6,
"xframe": 7,
"restifyCookieParser": 15,
"fooHandler": 23,
"barHandler": 14,
"carHandler": 14
}
},
"res": {
"statusCode": 200,
"headers": {
"access-control-allow-origin": "*",
"access-control-allow-headers": "Accept, Accept-Version,
Content-Length, Content-MD5, Content-Type, Date, Api-Version",
"access-control-expose-headers": "Api-Version, Request-Id,
Response-Time",
"server": "Joyent SmartDataCenter 7.0.0",
"x-request-id": "ed634c3e-1af0-40e4-ad1e-68c2fb67c8e1",
"access-control-allow-methods": "GET",
"x-api-version": "1.0.0",
"connection": "close",
"content-length": 158,
"content-md5": "zkiRn2/k3saflPhxXI7aXA==",
"content-type": "application/json",
"date": "Tue, 07 Feb 2012 20:30:31 GMT",
"x-response-time": 1639
},
"trailer": false
},
"route": {
"name": "GetFoo",
"version": ["1.0.0"]
},
"secure": false,
"level": 30,
"msg": "GetFoo handled: 200",
"time": "2012-02-07T20:30:31.896Z",
"v": 0
}
```
The `timers` field shows the time each handler took to run in microseconds.
Restify by default will record this information for every handler for each
route. However, if you decide to include nested handlers, you can track the
timing yourself by utilizing the Request
[startHandlerTimer][55] and
[endHandlerTimer][56] API.
You can also listen to auditlog event and get same above log object when
log event emits. For example
```javascript
SERVER.on('auditlog', function (data) {
//do some process with log
});
```
Returns **[Function][35]** Handler
### metrics
The module includes the following plugins to be used with restify's `after`
event, e.g., `server.on('after', restify.plugins.metrics());`:
A plugin that listens to the server's after event and emits information
about that request.
**Parameters**
- `opts` **[Object][39]** an options obj
- `opts.server` **Server** restify server
- `callback` **createMetrics~callback** a callback fn
**Examples**
```javascript
server.on('after', restify.plugins.metrics({ server: server },
function (err, metrics, req, res, route) {
// metrics is an object containing information about the request
}));
```
Returns **[Function][35]** returns a function suitable to be used
with restify server's `after` event
## Types
### metrics~callback
Callback used by metrics plugin
Type: [Function][35]
**Parameters**
- `err` **[Error][54]**
- `metrics` **[Object][39]** metrics about the request
- `metrics.statusCode` **[Number][44]** status code of the response. can be
undefined in the case of an uncaughtException
- `metrics.method` **[String][41]** http request verb
- `metrics.totalLatency` **[Number][44]** latency includes both request is flushed
2018-03-20 11:10:28 -07:00
and all handlers finished
- `metrics.latency` **[Number][44]** latency when request is flushed
- `metrics.preLatency` **([Number][44] | null)** pre handlers latency
- `metrics.useLatency` **([Number][44] | null)** use handlers latency
- `metrics.routeLatency` **([Number][44] | null)** route handlers latency
- `metrics.path` **[String][41]** `req.path()` value
- `metrics.inflightRequests` **[Number][44]** Number of inflight requests pending
in restify.
- `metrics.unifinishedRequests` **[Number][44]** Same as `inflightRequests`
- `metrics.connectionState` **[String][41]** can be either `'close'` or
2018-03-20 11:10:28 -07:00
`undefined`. If this value is set, err will be a
corresponding `RequestCloseError`.
If connectionState is either
2018-03-20 11:10:28 -07:00
`'close'`, then the `statusCode` is not applicable since the
connection was severed before a response was written.
- `req` **[Request][57]** the request obj
- `res` **[Response][58]** the response obj
- `route` **Route** the route obj that serviced the request
## req.set
Set context value by key
Requires the context plugin.
**Parameters**
- `key` **[String][41]** key
- `value` **any** value
Returns **[undefined][59]** no return value
## req.get
Get context value by key.
Requires the context plugin.
**Parameters**
- `key` **[String][41]** key
Returns **any** value stored in context
## req.getAll
Get all context
Requires the context plugin.
Returns **any** value stored in context
[1]: #usage
[2]: #serverpre-plugins
[3]: #context
[4]: #dedupeslashes
[5]: #pause
[6]: #sanitizepath
[7]: #reqidheaders
[8]: #strictqueryparams
[9]: #useragentconnection
[10]: #serveruse-plugins
[11]: #acceptparser
[12]: #authorizationparser
[13]: #dateparser
[14]: #queryparser
[15]: #jsonp
[16]: #bodyparser
[17]: #requestlogger
[18]: #gzipresponse
[19]: #servestatic
[20]: #servestaticfiles
[21]: #throttle
[22]: #requestexpiry
[23]: #using-an-external-storage-mechanism-for-keybucket-mappings
[24]: #inflightrequestthrottle
[25]: #cpuusagethrottle
[26]: #conditionalhandler
[27]: #conditionalrequest
[28]: #auditlogger
[29]: #metrics
[30]: #types
[31]: #metricscallback
[32]: #reqset
[33]: #reqget
[34]: #reqgetall
[35]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[36]: https://github.com/restify/node-restify/issues/287
[37]: https://github.com/restify/node-restify/issues/409
[38]: https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips
[39]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[40]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[41]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[42]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp
[43]: https://github.com/joyent/node-http-signature
[44]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[45]: https://github.com/ljharb/qs
[46]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[47]: https://github.com/felixge/node-formidable
[48]: https://github.com/trentm/node-bunyan
[49]: #request-api
[50]: https://github.com/restify/node-restify/issues/284
[51]: https://github.com/pillarjs/send
[52]: http://en.wikipedia.org/wiki/Token_bucket
[53]: http://tools.ietf.org/html/draft-nottingham-http-new-status-03#section-4
[54]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[55]: #starthandlertimerhandlername
[56]: #endhandlertimerhandlername
[57]: https://developer.mozilla.org/Add-ons/SDK/High-Level_APIs/request
[58]: https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5
[59]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined