UNPKG

17.8 kBMarkdownView Raw
1# httpism [![npm version](https://img.shields.io/npm/v/httpism.svg)](https://www.npmjs.com/package/httpism) [![npm](https://img.shields.io/npm/dm/httpism.svg)](https://www.npmjs.com/package/httpism) [![Build Status](https://travis-ci.org/featurist/httpism.svg?branch=master)](https://travis-ci.org/featurist/httpism)
2
3httpism is a node and browser HTTP client that does a few things differently:
4
5* **middleware**: customise a HTTP client for your API by sticking together middleware, for example, for content handlers or authentication schemes.
6* **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.
7* **promises**: no messing about with callbacks.
8* for **browser** and **server** alike.
9
10In addition, httpism supports:
11
12* URL templates
13* Cookies
14* HTTP proxies for HTTP and HTTPS traffic, with proxy authentication
15* Basic authentication
16* JSON
17* URL encoded forms
18* streams
19* CORS
20* JSONP
21
22## Upgrading from 2.x
23
24Httpism 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:
25
26```js
27var httpism = require('httpism').client({response: true})
28```
29
30## NPM: [httpism](https://www.npmjs.org/package/httpism)
31
32```sh
33npm install httpism
34```
35
36Then
37
38```js
39var httpism = require('httpism');
40```
41
42Compatible with browserify and webpack too!
43
44## Browser Size
45
46* httpism.js: 23K
47* httpism.min.js: 10K
48* httpism.min.js.gz: 3.7K
49
50## GET JSON
51
52```js
53httpism.get('http://example.com/').then(function (responseBody) {
54 console.log('json', responseBody);
55}, function (error) {
56 console.log('uh oh', error);
57});
58```
59
60## POST JSON
61
62```js
63httpism.post('http://example.com/', {name: 'Betty Boop'}).then(function (responseBody) {
64 console.log('json', responseBody);
65}, function (error) {
66 console.log('uh oh', error);
67});
68```
69
70## POST www-form-urlencoded
71
72```js
73httpism.post('http://example.com/', { name: "Betty Boop" }, { form: true }).then(function (responseBody) {
74 console.log('json', responseBody);
75}, function (error) {
76 console.log('uh oh', error);
77});
78```
79
80## POST streams and files
81
82Pass 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.
83
84```js
85var stream = fs.createReadStream('afile.txt');
86
87httpism.post('http://example.com/', stream).then(function (responseBody) {
88 console.log('json', responseBody);
89}, function (error) {
90 console.log('uh oh', error);
91});
92```
93
94## POST multipart forms
95
96Httpism works with [form-data](https://github.com/form-data/form-data), all you need to do is pass a `FormData` instance as the body:
97
98```js
99var form = new FormData();
100
101form.append('name', 'Betty Boop');
102form.append('address', 'New York');
103form.append('photo', fs.createReadStream('betty.jpg'));
104
105httpism.post('http://example.com/', form).then(function (responseBody) {
106 console.log('json', responseBody);
107}, function (error) {
108 console.log('uh oh', error);
109});
110```
111
112## Create an API client
113
114Specify a base URL:
115
116```js
117var example = httpism.client('http://example.com/');
118
119// GET http://example.com/a
120example.get('a').then(function (responseBody) {
121 console.log(responseBody);
122});
123```
124
125Specify some options:
126
127```js
128var loggingHttpism = httpism.client({exceptions: false});
129
130loggingHttpism.get('http://example.com/').then(function (responseBody) {
131 console.log(responseBody);
132});
133```
134
135Add some middleware:
136
137```js
138var authHttpism = httpism.client(function (request, next) {
139 request.url += '?apikey=myapikey';
140 return next();
141});
142
143// GET https://secretapi.com/?apikey=myapikey
144authHttpism.get('https://secretapi.com/').then(function (responseBody) {
145 console.log(responseBody);
146});
147```
148
149See more about [clients](#clients).
150
151## In the Browser
152
153The browser version has a few differences from the node version:
154
155* Relative URLs are relative to the current browser location.
156* No support for streams.
157* Redirects aren't optional, browsers _always_ follow redirects.
158* Logging is removed, since most (if not all?) browsers now have a network debug tab.
159
160However, everything else works as described here.
161
162## Debug
163
164httpism uses [debug](https://github.com/visionmedia/debug) so you can enable logging just by setting the `DEBUG` environment variable to `httpism:*`:
165
166```bash
167DEBUG=httpism* node app.js
168```
169
170* `httpism` simple request => response, i.e. `GET http://www.example.com/api => 200 (40ms)`
171* `httpism:request` the request
172* `httpism:response` the response
173
174More information in debug's README.
175
176## Proxy Environment Variables
177
178Httpism obeys the following environments variables:
179
180 * `http_proxy` `HTTP_PROXY` - for HTTP requests
181 * `https_proxy` `HTTPS_PROXY` - for HTTPS requests
182 * `all_proxy` `ALL_PROXY` - for HTTP or HTTPS requests
183 * `no_proxy` `NO_PROXY` - an comma separated list of hostnames (and optional ports) to not proxy
184
185For more details please see [proxy-from-env](https://github.com/Rob--W/proxy-from-env).
186
187## Requests
188
189### GET, HEAD, DELETE
190
191```js
192httpism.method(url, [options])
193```
194
195* `url` a string URL, this is a URL template if the `params` option is used, see [params](#params).
196* `options` request options, see [options](#options).
197
198returns a promise
199
200### POST, PUT, PATCH, OPTIONS
201
202```js
203httpism.method(url, body, [options])
204```
205
206* `url` a string URL, this is a URL template if the `params` option is used, see [params](#params).
207* `body` the request body to send
208 * by default a JS object is encoded as JSON and sent as `application/json`
209 * a JS object with options `{form: true}` is url-encoded and sent as `application/x-www-form-urlencoded`
210 * a stream. It will try to guess the `Content-Type` from a file stream, but if not, pass `{headers: {'content-type': ...}}` as options.
211* `options` request options, see [options](#options).
212
213### Params
214
215Httpism 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.
216
217```js
218httpism.get('http://example.com/users/:user/posts', {
219 params: {
220 user: 'bob',
221 page: 3,
222 search: 'lakes'
223 }
224})
225```
226
227Will become
228
229```
230GET http://example.com/users/bob/posts?page=3&search=lakes
231```
232
233A template contains two forms of parameter, varying on the way special characters are encoded for URLs.
234
235* `:param` - uses `encodeURIComponent`, and is useful for most applications
236* `:param*` - uses `encodeURI` and can be used to interpolate paths, such as `a/path/to/something` without encoding the slash characters.
237
238Any remaining parameters will be encoded in the query string, you can override how the query string is encoded using the `qs` option.
239
240The template interpolation itself can be overridden with the `expandUrl` option, and is used as follows:
241
242```js
243var url = expandUrl(template, params, querystring)
244```
245
246* `template` - the URL template, passed in as the `url` argument to `httpism.get`, etc.
247* `params` - the object containing the parameters to be interpolated.
248* `querystring` - the `qs` option, can be used to encode the query string parameters, e.g. `querystring.stringify(params)`.
249
250For example, you could use RFC 6570 templates like this
251
252```js
253var urlTemplate = require('url-template')
254
255function expandUrl(url, params) {
256 var template = urlTemplate.parse(url)
257 return template.expand(params)
258}
259
260httpism.get('http://example.com/users/{user}/posts{?page,search}', {
261 params: {
262 user: 'bob',
263 page: 3,
264 search: 'lakes'
265 },
266 expandUrl: expandUrl
267})
268```
269
270Or indeed create a new client to use this by default:
271
272```js
273var httpism = require('httpsim').client({
274 expandUrl: expandUrl
275})
276
277httpism.get('http://example.com/users/{user}/posts{?page,search}')
278```
279
280### Request
281
282```js
283httpism.request(method, url, [body], [options])
284```
285
286* `url` a string url, full or relative to the response, or '' to request the response again
287* `body` the request body to send
288 * by default a JS object is encoded as JSON and sent as `application/json`
289 * a JS object with options `{form: true}` is url-encoded and sent as `application/x-www-form-urlencoded`
290 * a stream. It will try to guess the `Content-Type` from a file stream, but if not, pass `{headers: {'content-type': ...}}` as options.
291* `options` request options, see [options](#options).
292
293
294## Responses
295
296Responses 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:
297
298* `statusCode` the status code as an integer, such as `200`, or `404`.
299* `statusText` the status text, such as `OK` or `Not Found`.
300* `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.
301* `headers` the headers of the response
302* `body` the body of the response. Depending on the `Content-Type` header:
303 * `application/json` a object
304 * `application/x-www-form-urlencoded` a object
305 * `text/*` or `application/javascript` a string
306 * 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.
307
308## Cookies
309
310Cookies on the server are not handled by default, but you can enable them by using `httpism.client` passing the `{cookies: true}` option:
311
312```js
313var client = httpism.client({cookies: true});
314
315var creds = {
316 username: 'jerome',
317 password: 'password123'
318}
319
320client.post('http://example.com/login', creds, {form: true}).then(function () {
321 return client.get('/profile').then(function (profile) {
322 console.log(profile)
323 })
324})
325```
326
327Different httpism clients will use different cookie jars. Cookies are always on in the browser, using native browser cookies.
328
329## Cancel a request
330
331Requests can be cancelled by calling `.abort()` on the promise returned from any request method:
332
333```js
334var promise = httpism.get('/something');
335promise.abort();
336```
337
338## Options
339
340* `response`: default `false`, if true, returns the whole response, including URL, headers, status code and the body, otherwise return just the body.
341* `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.
342* `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.
343* `headers`: default `undefined`, can be set to an object that is merged with middleware headers.
344* `basicAuth`: use Basic Authentication, pass an object `{ username: 'bob', password: "bob's secret" }`.
345* `cookies`: default `false`, use cookies.
346* `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.
347* `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.
348* `form`: when `true`, treats the incoming JSON data as a form and encodes it as `application/x-www-form-urlencoded`.
349* `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:
350 * `'stream'`: always downloads the response as a stream
351 * `'json'`: always parses the response as a JSON object
352 * `'text'`: always parses the response as text
353 * `'form'`: always parses the response as a URL-encoded form
354 * `undefined`: parse response based on `Content-Type`, the default.
355* `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.
356* `http`: default `undefined`, object containing options that are passed to [Node.js http.request()](http://nodejs.org/api/http.html#http_http_request_options_callback).
357 Many of these options are ignored by default, so you should set `agent: undefined` to force a new agent to honour the options.
358* `https`: default `undefined`, object containing options that are passed to [Node.js https.request()](http://nodejs.org/api/https.html#https_https_request_options_callback).
359 Many of these options are ignored by default, so you should set `agent: undefined` to force a new agent to honour the options.
360* `jsonp`: to perform a JSONP request, set this to the name of the parameter to contain the callback function, often this is simply `callback`.
361* `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()`.
362* `jsonReviver`: a [reviver function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) that is passed to `JSON.parse(string, [reviver])` to override how JSON response bodies are decoded.
363* `timeout`: the request timeout in milliseconds.
364* `output`: should be a stream, the response body will be written to the stream and httpism will wait until it's fully written.
365
366## Clients
367
368Clients 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.
369
370You 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.
371
372```js
373var client = httpism.client([url], [options], [middleware]);
374var client = httpism.raw.client([url], [options], [middleware]);
375var anotherClient = client.client([url], [options], [middleware]);
376```
377
378* `url` a URL string, which could be relative to the response, or absolute.
379* `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.
380* `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). Middleware specified on the new client is _prepended_ to the middleware currently in the client.
381
382* `httpism` is the basic client, with all the goodies described above.
383* `httpism.raw` is a raw client that has only the base transport, `http` or `https` on node, and `xhr` in the browser.
384
385## Middleware
386
387Middleware commonly works like this:
388
389```js
390function middleware(request, next, client) {
391 // change request
392 request.url = ...;
393 return next().then(function (response) {
394 // change response
395 response.body = ...;
396 return response;
397 });
398}
399```
400
401Middleware 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.
402
403```js
404middleware.httpismMiddleware = {
405 name: 'name',
406 before: ['http', 'debugLog'],
407 after: 'redirect'
408}
409```
410
411You can insert the middleware by passing it to `httpism.client()`, or by calling `client.use()`:
412
413```js
414// create a new client with middleware
415var client = httpism.client(middleware);
416
417// add middleware to an existing client
418client.use(middleware);
419
420// add middleware globally and to all new clients
421httpism.use(middleware);
422```
423
424* `request` is an object with the following properties:
425 * `url` the full URL of the request, e.g. `http://example.com/path?query=value`
426 * `method` the method of the request, e.g. `GET` or `POST`
427 * `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' }`
428 * `options` the [options](#options) as passed through from the request, either from the **client** or the individual request. E.g. `{exceptions: true}`.
429 * `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()`.
430* `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](#responses).
431* `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.
432* `middleware.middleware` is the name of the middleware, which can be referred to by other middlewares when adding themselves with `before` or `after`.
433* `middleware.before` ensure that the middleware is inserted just before the named middleware.
434* `middleware.after` ensure that the middleware is inserted just after the named middleware.
435
436Middleware is stored in an array `client.middleware`, you're free to manipulate this directly.
437
438See the [middleware](middleware) directory for a full list of existing middleware.
439
440# License
441
442BSD