A fully-featured REST client built for ease-of-use and resilience
- Circuit breaker
- Caching
- Retries
- Rate Limiting
- Timeout
- Logging
- Parses JSON responses
- Understands HTTP errors
- StatsD integration
- Custom HTTP client
By default the client implements a circuit breaker using the Levee library. It is configured to trip after 100 failures and resets after 10 seconds. This can be configured using the circuitBreakerMaxFailures
and circuitBreakerResetTimeout
properties.
For example to trip after 200 failures and try to reset after 30 seconds:
const restClient = require('flashheart');
const StatsD = require('node-statsd');
const client = restClient.createClient({
name: 'my-client',
circuitbreaker: {
maxFailures: 200,
resetTimeout: 30000
}
});
If caching is enabled, the client will cache response with a max-age
directive. You can specify the caching storage with an instance of Catbox using the cache
parameter.
const Catbox = require('catbox').Client;
const Memory = require('catbox-memory');
const storage = new Catbox(new Memory());
const flashheart = require('flashheart')
const client = flashheart.createClient({
name: 'my-client',
externalCache: {
cache: storage
},
// varyOn: [ 'accept-language' ] - optional, refer to https://github.com/bbc/http-transport-cache#cache-key-structure
});
The staleIfError
directive is also supported. If a response has a staleIfError
directive, the response will be cached for the duration of the stale-if-error
directive as well as the max-age
and will try to retrieve them in this order:
max-age
stored version fresh versionstale-if-error
stale version
By default the client retries failed requests once, with a delay of 100 milliseconds between attempts. The number of times to retry and the delay between retries can be configured using the retries
and retryDelay
properties.
For example, to retry 10 times, with a delay of 500ms:
const restClient = require('flashheart');
const StatsD = require('node-statsd');
const client = restClient.createClient({
name: 'my-client',
retries: 10,
retryDelay: 500
});
Only request errors or server errors result in a retry; 4XX
errors are not retried.
The client has no rate limitation by default. You can specify how many requests are allowed to happen within a given interval - respectively with the rateLimit
and rateLimitInterval
properties.
const restClient = require('flashheart');
const client = restClient.createClient({
rateLimit: 10, // Allow a maxmimum of 10 requests…
rateLimitInterval: 6000, // In an interval of 6 seconds (6000ms)
});
Note: rate limiting is provided by simple-rate-limiter.
The client has a default timeout of 2 seconds. You can override this when creating a client by setting the timeout
property.
const flashheart = require('flashheart');
const client = flashheart.createClient({
timeout: 50
});
All requests can be logged at info
level if you provide a logger that supports the standard logging API (like console
or Winston)
const flashheart = require('flashheart');
const client = flashheart.createClient({
logger: console
});
The client assumes you're working with a JSON API by default. It uses the json: true
option in request (default client) to send the Accept: application/json
header and automatically parse the response into an object. If you need to call an API that returns plain text, XML, animated GIFs etc. then set the json
flag to false
in your request options.
Any response with a status code greater than or equal to 400
results in an error. There's no need to manually check the status code of the response. The status code is exposed as err.statusCode
and the headers are assigned to err.headers
.
Metrics can be sent to StatsD by providing an instance of the node-statsd client:
const StatsD = require('node-statsd');
const stats = new StatsD();
const flashheart = require('flashheart');
const client = flashheart.createClient({
stats: stats
});
The following metrics are sent from each client:
Name | Type | Description |
---|---|---|
{name}.requests |
Counter | Incremented every time a request is made |
{name}.responses.{code} |
Counter | Incremented every time a response is received |
{name}.request_errors |
Counter | Incremented every time a request fails (timeout, DNS lookup fails etc.) |
{name}.response_time |
Timer | Measures of the response time in milliseconds across all requests |
{name}.retries |
Counter | Incremented every time the request retries |
{name}.attempts |
Timer | Measures the number of attempts |
{name}.cache.hits |
Counter | Incremented for each cache hit |
{name}.cache.misses |
Counter | Incremented for each cache miss |
{name}.cache.errors |
Counter | Incremented whenever there's is a problem communicating with the cache |
The {name}
variable comes from the name
option you pass to createClient
. It defaults to http
if you don't name your client.
By default, Flashheart uses request under the hood and configures it with a set of default values. You can however, configure and pass in your own instance of request (for example, if you need to use certificates). This can be done by setting httpClient
property when creating Flashheart client. Please also note that in order for this to work, you need to use another layer of abstraction and pull in @bbc/http-transport.
const restClient = require('flashheart');
const request = require('request');
const httpTransport = require('@bbc/http-transport');
const requestClient = request.defaults({
json: true,
timeout: 1000,
time: true,
gzip: true,
forever: true,
agentOptions: {
maxSockets: 1000
},
strictSSL: true,
cert: fs.readFileSync('./client.crt'),
key: fs.readFileSync('./client.key'),
ca: fs.readFileSync('./client.crt')
});
const RequestTransport = httpTransport.RequestTransport;
const httpClient = new RequestTransport(requestClient);
const client = restClient.createClient({
name: 'my-client',
httpClient
});