HTTP client with middleware and good defaults


Keywords
http, rest
License
BSD-2-Clause
Install
npm install httpism@3.26.0

Documentation

httpism npm version npm Build Status

httpism is a node and browser HTTP client that does a few things differently:

  • middleware: customise a HTTP client for your API by sticking together middleware, for example, for content handlers or authentication schemes.
  • useful by default: sends and receives JSON, throws exceptions on 400-500s, follows redirects. Of course, you can disable this stuff when it gets in your way, or hit raw HTTP and streams when you need to get clever.
  • promises: no messing about with callbacks.
  • for browser and server alike.

In addition, httpism supports:

  • URL templates
  • Cookies
  • HTTP proxies for HTTP and HTTPS traffic, with proxy authentication
  • Basic authentication
  • JSON
  • URL encoded forms
  • streams
  • CORS
  • JSONP

Upgrading from 2.x

Httpism 3.x returns the body of the response by default, not the response. This is what you want 95% of the time, however, if you're upgrading from 2.x, or you want the response with headers, status code, etc, then you can do this:

var httpism = require('httpism').client({response: true})

NPM: httpism

npm install httpism

Then

var httpism = require('httpism');

Compatible with browserify and webpack too!

Browser Size

  • httpism.js: 31K
  • httpism.min.js: 13K
  • httpism.min.js.gz: 4.6K

GET JSON

httpism.get('http://example.com/').then(function (responseBody) {
  console.log('json', responseBody);
}, function (error) {
  console.log('uh oh', error);
});

POST JSON

httpism.post('http://example.com/', {name: 'Betty Boop'}).then(function (responseBody) {
  console.log('json', responseBody);
}, function (error) {
  console.log('uh oh', error);
});

POST www-form-urlencoded

httpism.post('http://example.com/', { name: "Betty Boop" }, { form: true }).then(function (responseBody) {
  console.log('json', responseBody);
}, function (error) {
  console.log('uh oh', error);
});

POST streams and files

Pass a stream as the second argument, it will try to guess the Content-Type from the filename if possible, but you can override it if you know better.

var stream = fs.createReadStream('afile.txt');

httpism.post('http://example.com/', stream).then(function (responseBody) {
  console.log('json', responseBody);
}, function (error) {
  console.log('uh oh', error);
});

POST multipart forms

Httpism works with form-data, all you need to do is pass a FormData instance as the body:

var form = new FormData();

form.append('name', 'Betty Boop');
form.append('address', 'New York');
form.append('photo', fs.createReadStream('betty.jpg'));

httpism.post('http://example.com/', form).then(function (responseBody) {
  console.log('json', responseBody);
}, function (error) {
  console.log('uh oh', error);
});

Create an API client

Specify a base URL:

var example = httpism.client('http://example.com/');

// GET http://example.com/a
example.get('a').then(function (responseBody) {
  console.log(responseBody);
});

Specify some options:

var loggingHttpism = httpism.client({exceptions: false});

loggingHttpism.get('http://example.com/').then(function (responseBody) {
  console.log(responseBody);
});

Add some middleware:

var authHttpism = httpism.client(function (request, next) {
  request.url += '?apikey=myapikey';
  return next();
});

// GET https://secretapi.com/?apikey=myapikey
authHttpism.get('https://secretapi.com/').then(function (responseBody) {
  console.log(responseBody);
});

See more about clients.

In the Browser

The browser version has a few differences from the node version:

  • Relative URLs are relative to the current browser location.
  • No support for streams.
  • Redirects aren't optional, browsers always follow redirects.
  • Logging is removed, since most (if not all?) browsers now have a network debug tab.

However, everything else works as described here.

Debug

httpism uses debug so you can enable logging just by setting the DEBUG environment variable to httpism:*:

DEBUG=httpism* node app.js
  • httpism simple request => response, i.e. GET http://www.example.com/api => 200 (40ms)
  • httpism:error only errors, shown in simple request => response, i.e. GET http://www.example.com/api => 500 (40ms)
  • httpism:request the full request including body
  • httpism:response the full response including body
  • httpism:response:error only errors, the full response including body

More information in debug's README.

Proxy Environment Variables

Httpism obeys the following environments variables:

  • http_proxy HTTP_PROXY - for HTTP requests
  • https_proxy HTTPS_PROXY - for HTTPS requests
  • all_proxy ALL_PROXY - for HTTP or HTTPS requests
  • no_proxy NO_PROXY - an comma separated list of hostnames (and optional ports) to not proxy

For more details please see proxy-from-env.

Requests

GET, HEAD, DELETE

httpism.method(url, [options])
  • url a string URL, this is a URL template if the params option is used, see params.
  • options request options, see options.

returns a promise

POST, PUT, PATCH, OPTIONS

httpism.method(url, body, [options])
  • url a string URL, this is a URL template if the params option is used, see params.
  • body the request body to send
    • by default a JS object is encoded as JSON and sent as application/json
    • a JS object with options {form: true} is url-encoded and sent as application/x-www-form-urlencoded
    • a stream. It will try to guess the Content-Type from a file stream, but if not, pass {headers: {'content-type': ...}} as options.
  • options request options, see options.

Params

Httpism will render a URL template if the params option is used, the params are interpolated into the URL template, any params left over will form the query string.

httpism.get('http://example.com/users/:user/posts', {
  params: {
    user: 'bob',
    page: 3,
    search: 'lakes'
  }
})

Will become

GET http://example.com/users/bob/posts?page=3&search=lakes

A template contains two forms of parameter, varying on the way special characters are encoded for URLs.

  • :param - uses encodeURIComponent, and is useful for most applications
  • :param* - uses encodeURI and can be used to interpolate paths, such as a/path/to/something without encoding the slash characters.

Any remaining parameters will be encoded in the query string, you can override how the query string is encoded using the qs option.

The template interpolation will throw an error if one of the :param or :param* parameters are given an undefined value.

The template interpolation itself can be overridden with the expandUrl option, and is used as follows:

var url = expandUrl(template, params, querystring)
  • template - the URL template, passed in as the url argument to httpism.get, etc.
  • params - the object containing the parameters to be interpolated.
  • querystring - the qs option, can be used to encode the query string parameters, e.g. querystring.stringify(params).

For example, you could use RFC 6570 templates like this

var urlTemplate = require('url-template')

function expandUrl(url, params) {
  var template = urlTemplate.parse(url)
  return template.expand(params)
}

httpism.get('http://example.com/users/{user}/posts{?page,search}', {
  params: {
    user: 'bob',
    page: 3,
    search: 'lakes'
  },
  expandUrl: expandUrl
})

Or indeed create a new client to use this by default:

var httpism = require('httpsim').client({
  expandUrl: expandUrl
})

httpism.get('http://example.com/users/{user}/posts{?page,search}')

Request

httpism.request(method, url, [body], [options])
  • url a string url, full or relative to the response, or '' to request the response again
  • body the request body to send
    • by default a JS object is encoded as JSON and sent as application/json
    • a JS object with options {form: true} is url-encoded and sent as application/x-www-form-urlencoded
    • a stream. It will try to guess the Content-Type from a file stream, but if not, pass {headers: {'content-type': ...}} as options.
  • options request options, see options.

Responses

Responses bodies are returned by all methods by default. To access other details about responses, pass { response: true } in the request options to receive a response object that contains:

  • statusCode the status code as an integer, such as 200, or 404.
  • statusText the status text, such as OK or Not Found.
  • url the full URL of the response. In the browser, this will be root-relative if the request is for the same domain as the current page. This can be different to the request.url if there was a redirect.
  • headers the headers of the response
  • body the body of the response. Depending on the Content-Type header:
    • application/json a object
    • application/x-www-form-urlencoded a object
    • text/* or application/javascript a string
    • on the server, anything else is returned as a Node stream, be careful to close it!. In the browser, anything else is returned as a string.

Cookies

Cookies on the server are not handled by default, but you can enable them by using httpism.client passing the {cookies: true} option:

var client = httpism.client({cookies: true});

var creds = {
  username: 'jerome',
  password: 'password123'
}

client.post('http://example.com/login', creds, {form: true}).then(function () {
  return client.get('/profile').then(function (profile) {
    console.log(profile)
  })
})

Different httpism clients will use different cookie jars. Cookies are always on in the browser, using native browser cookies.

Cancel a request

Requests can be cancelled by calling .abort() on the promise returned from any request method:

var promise = httpism.get('/something');
promise.abort();

Options

  • response: default false, if true, returns the whole response, including URL, headers, status code and the body, otherwise return just the body.
  • exceptions: default true, throw exceptions on reception of 400-500 status codes. Set to false to simply return the response. If set to a function, the function is passed the response, and returns true to throw the response as an exception, or false to treat it as a normal response.
  • redirect: default true, follow redirects for 300, 301, 302, 303 and 307 status codes with Location response headers. Set to false to simply return the redirect response.
  • headers: default undefined, can be set to an object that is merged with middleware headers.
  • basicAuth: use Basic Authentication, pass an object { username: 'bob', password: "bob's secret" }.
  • cookies: default false, use cookies.
  • querystring: default undefined, can be set to an object containing fields that are URL-encoded and merged with the querystring already on the URL, if any. This is parsed and stringified using options.qs.parse and options.qs.stringify if provided, or using a very lite internal query string parser.
  • qs: optional override for parsing and stringifying querystrings, you can pass node's querystring or qs. Any object that contains the methods parse and stringify can be used. If not provided, httpism will use an internal (and very small) query string parser/stringifier.
  • form: when true, treats the incoming JSON data as a form and encodes it as application/x-www-form-urlencoded.
  • responseBody: can be used to force the parsing of the response, ignoring the Content-Type, it can be a string of one of the following:
    • 'stream': always downloads the response as a stream
    • 'json': always parses the response as a JSON object
    • 'text': always parses the response as text
    • 'form': always parses the response as a URL-encoded form
    • undefined: parse response based on Content-Type, the default.
  • proxy: a proxy URL, if present all requests will be run through the proxy. This works if either of the environment variables http_proxy or HTTP_PROXY are set too.
  • http: default undefined, object containing options that are passed to Node.js http.request(). Many of these options are ignored by default, so you should set agent: undefined to force a new agent to honour the options.
  • https: default undefined, object containing options that are passed to Node.js https.request(). Many of these options are ignored by default, so you should set agent: undefined to force a new agent to honour the options.
  • jsonp: to perform a JSONP request, set this to the name of the parameter to contain the callback function, often this is simply callback.
  • xhr: can be used to override window.XMLHttpRequest used to make the request, useful for mocking out requests during testing. It is expected to be used as a constructor, as in new options.xhr().
  • jsonReviver: a reviver function that is passed to JSON.parse(string, [reviver]) to override how JSON response bodies are decoded.
  • timeout: the request timeout in milliseconds.
  • output: should be a stream, the response body will be written to the stream and httpism will wait until it's fully written.

Clients

Clients give you a way to build or customise a HTTP client for the purpose of accessing a particular web API. Web APIs will often have special authorization, headers, or URL conventions that are common across all calls, and you only want to have to specify those things once.

You can create API clients, either from httpism, giving you a fairly complete HTTP client, or from httpism.raw giving you no frills streaming HTTP client to do what you will with.

var client = httpism.client([url], [options], [middleware]);
var client = httpism.raw.client([url], [options], [middleware]);
var anotherClient = client.client([url], [options], [middleware]);
  • url a URL string, which could be relative to the response, or absolute.

  • options options object to be used for all calls with this client. If client is called on a response, the options are merged with that responses client.

  • middleware a middleware function or array of middleware functions. Requests in middleware are processed from the beginning of the array to the end, and responses from the end of the array to the beginning. See middleware. Middleware specified on the new client is prepended to the middleware currently in the client.

  • httpism is the basic client, with all the goodies described above.

  • httpism.raw is a raw client that has only the base transport, http or https on node, and xhr in the browser.

Middleware

Middleware commonly works like this:

function middleware(request, next, client) {
  // change request
  request.url = ...;
  return next().then(function (response) {
    // change response
    response.body = ...;
    return response;
  });
}

Middleware are ordered, and each one can have a name, and a preference to be placed before or after other named middleware. You can place the middleware before any of the middleware in an array, or after any of the middleware in an array.

middleware.httpismMiddleware = {
  name: 'name',
  before: ['http', 'debugLog'],
  after: 'redirect'
}

You can insert the middleware by passing it to httpism.client(), or by calling client.use():

// create a new client with middleware
var client = httpism.client(middleware);

// add middleware to an existing client
client.use(middleware);

// add middleware globally and to all new clients
httpism.use(middleware);
  • request is an object with the following properties:
    • url the full URL of the request, e.g. http://example.com/path?query=value
    • method the method of the request, e.g. GET or POST
    • headers the headers of the request as an object. All headers are lower-cased as per Node.js conventions. E.g. { 'content-type': 'application/json' }
    • options the options as passed through from the request, either from the client or the individual request. E.g. {exceptions: true}.
    • body the body of the request. Will be undefined for get() etc, otherwise will be the object specified as the second argument to methods like post().
  • next([request]) is a function that passes control onto the next middleware, optionally taking a request parameter. If the request parameter is not given it uses the request passed in to the middleware. It returns a promise of the response.
  • client is a httpism client object, for which you can make further requests inside the middleware with client.request(request). For example, the redirect middleware uses this.
  • middleware.middleware is the name of the middleware, which can be referred to by other middlewares when adding themselves with before or after.
  • middleware.before ensure that the middleware is inserted just before the named middleware.
  • middleware.after ensure that the middleware is inserted just after the named middleware.

Middleware is stored in an array client.middleware, you're free to manipulate this directly.

See the middleware directory for a full list of existing middleware.

License

BSD

We're Hiring!

Join our remote team and help us build amazing software. Check out our career opportunities.