Simple HTTP client with failover functionality for node.js and browsers
It supports simple fallback addresses as well as dynamic service discovery using Consul.
Multiagent uses superagent under the covers and exposes most of its API as well as an additional promise interface (if native promises are available).
All browsers with ECMAScript 5 support should work.
node.js, browserify, webpack:
npm install --save multiagent
as a global script in the browser:
<script src="node_modules/multiagent/lib/browser.min.js"></script>
from a CDN:
<script src="https://unpkg.com/multiagent/lib/browser.min.js"></script>
In case you load multiagent with a script reference into the browser,
it will create the global variable multiagent
.
Multiagent can be used as a simple HTTP client, pretty much as a drop-in replacement of superagent:
const agent = require('multiagent');
// create a request:
const req = agent.request('GET', 'http://domain.com');
// or use a shorthand (there's also: 'head', 'post', 'put', 'delete'/'del')
const req = agent.get('http://domain.com');
// execute a request, providing a callback:
req.end((err, res) => console.log(err || res.body));
// or instead, use the promise interface:
const promise = req.promise();
promise.then(res => console.log(res.body), err => console.log(err));
// you can also simply just call 'then' (or 'catch') on the request:
req.then(res => console.log(res.body), err => console.log(err));
If you have your service running on multiple endpoints, you can provide a list of hard-coded server URLs and take advantage of multiagent's failover mechanism:
const agent = require('multiagent');
// create a client:
const client = agent.client({
servers: ['http://sub1.abc.com', 'http://sub2.abc.com', 'http://sub3.abc.com']
});
// then do stuff:
client
.get('/endpoint') // use just the path without host!
.timeout(500) // used per individual call!
.end((err, res) => console.log(err || res.body));
Instead of hard-coding your server URLs you can use a Consul server or cluster to dynamically resolve the base URLs for your service calls:
const agent = require('multiagent');
// create a client:
const client = agent.client({
discovery: 'consul', // only possible value at the moment, more could be added in the future
discoveryServers: ['http://consul1.abc.com', 'http://consul2.abc.com', 'http://consul3.abc.com'],
serviceName: 'my-service'
});
// then do stuff:
client
.get('/endpoint') // use just the path without host!
.timeout(500) // used per individual service call!
.end((err, res) => console.log(err || res.body));
If you're just interested in the service URLs e.g. from Consul without actually calling any
service endpoint, you can use the resolveServers
function provided by the client instance:
const agent = require('multiagent');
// create a client:
const client = agent.client({
discovery: 'consul',
discoveryServers: ['http://consul1.abc.com', 'http://consul2.abc.com', 'http://consul3.abc.com'],
serviceName: 'my-service'
});
// get the list of servers providing a callback:
client.resolveServers((err, servers) => console.log(err || servers));
// or use the promise interface:
client.resolveServers().then(servers => console.log(servers));
For the client using simple failover you can pass the following additional options:
- strategy: string, (sequentially|randomly|simultaneously), default: 'sequentially'
- shouldFailover: function, default:
(err, res) => err || res.status >= 400
For the client using Consul you can pass the following additional options:
- serviceProtocol: string, (http|https), default: 'http'
- serviceStrategy: string, (sequentially|randomly|simultaneously), default: 'sequentially'
- discoveryTimeout: number, in milliseconds, default: 2000
- discoveryStrategy: string, (sequentially|randomly|simultaneously), default: 'simultaneously'
- refreshAfter: number, in milliseconds, default: 60000
- shouldFailover: function, default:
(err, res) => err || res.status >= 400
- createConsulRequestPath: function, default:
serviceName => `/v1/health/service/${encodeURIComponent(serviceName)}?passing=true`
, - createServerListFromConsulResponse: function, default:
(body, serviceProtocol) => body.map(x => `${serviceProtocol}://${x.Service.Address}:${x.Service.Port}`)
When you create a client with failover using agent.client({ /* options */ })
you can override
the failover strategy as well as the failover criteria on a per request basis. This is sometimes useful
when e.g. in a RESTful environment a response with status code 404 (not found) is a perfectly valid
result and should not lead to any failover:
// create a client with default options for all requests issued using this client instance:
const client = agent.client({
servers: ['http://sub1.abc.com', 'http://sub2.abc.com', 'http://sub3.abc.com'],
strategy: 'simultaneously',
shouldFailover: (err, res) => err || res.status >= 400
});
// this will execute the requests sequentially and NOT failover on a 404 status response,
// thus overriding the options 'strategy' and 'shouldFailover' set as default on the client:
client
.get('/endpoint')
.failover({ strategy: 'sequentially' })
.failover({ shouldFailover: (err, res) => err || (res.status >= 400 && res.status !== 404) })
The following functions from superagent are supported:
- head
- get
- post
- put
- delete
- del
Additionally:
- request
- resolveServers
- set
- query
- send
- type
- accept
- timeout
- auth
- redirects
- attach
- field
- withCredentials
- abort
- end
Additionally:
- failover
- promise
- then
- catch
MIT