UNPKG

30.4 kBMarkdownView Raw
1
2# SuperAgent
3
4SuperAgent is light-weight progressive ajax API crafted for flexibility, readability, and a low learning curve after being frustrated with many of the existing request APIs. It also works with Node.js!
5
6 request
7 .post('/api/pet')
8 .send({ name: 'Manny', species: 'cat' })
9 .set('X-API-Key', 'foobar')
10 .set('Accept', 'application/json')
11 .then(res => {
12 alert('yay got ' + JSON.stringify(res.body));
13 });
14
15## Test documentation
16
17The following [test documentation](docs/test.html) was generated with [Mocha's](https://mochajs.org/) "doc" reporter, and directly reflects the test suite. This provides an additional source of documentation.
18
19## Request basics
20
21A request can be initiated by invoking the appropriate method on the `request` object, then calling `.then()` (or `.end()` [or `await`](#promise-and-generator-support)) to send the request. For example a simple __GET__ request:
22
23 request
24 .get('/search')
25 .then(res => {
26 // res.body, res.headers, res.status
27 })
28 .catch(err => {
29 // err.message, err.response
30 });
31
32HTTP method may also be passed as a string:
33
34 request('GET', '/search').then(success, failure);
35
36Old-style callbacks are also supported, but not recommended. *Instead of* `.then()` you can call `.end()`:
37
38 request('GET', '/search').end(function(err, res){
39 if (res.ok) {}
40 });
41
42Absolute URLs can be used. In web browsers absolute URLs work only if the server implements [CORS](#cors).
43
44 request
45 .get('https://example.com/search')
46 .then(res => {
47
48 });
49
50The __Node__ client supports making requests to [Unix Domain Sockets](https://en.wikipedia.org/wiki/Unix_domain_socket):
51
52 // pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
53 // Use `%2F` as `/` in SOCKET_PATH
54 try {
55 const res = await request
56 .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search');
57 // res.body, res.headers, res.status
58 } catch(err) {
59 // err.message, err.response
60 }
61
62__DELETE__, __HEAD__, __PATCH__, __POST__, and __PUT__ requests can also be used, simply change the method name:
63
64 request
65 .head('/favicon.ico')
66 .then(res => {
67
68 });
69
70__DELETE__ can be also called as `.del()` for compatibility with old IE where `delete` is a reserved word.
71
72The HTTP method defaults to __GET__, so if you wish, the following is valid:
73
74 request('/search', (err, res) => {
75
76 });
77
78## Setting header fields
79
80Setting header fields is simple, invoke `.set()` with a field name and value:
81
82 request
83 .get('/search')
84 .set('API-Key', 'foobar')
85 .set('Accept', 'application/json')
86 .then(callback);
87
88You may also pass an object to set several fields in a single call:
89
90 request
91 .get('/search')
92 .set({ 'API-Key': 'foobar', Accept: 'application/json' })
93 .then(callback);
94
95## `GET` requests
96
97The `.query()` method accepts objects, which when used with the __GET__ method will form a query-string. The following will produce the path `/search?query=Manny&range=1..5&order=desc`.
98
99 request
100 .get('/search')
101 .query({ query: 'Manny' })
102 .query({ range: '1..5' })
103 .query({ order: 'desc' })
104 .then(res => {
105
106 });
107
108Or as a single object:
109
110 request
111 .get('/search')
112 .query({ query: 'Manny', range: '1..5', order: 'desc' })
113 .then(res => {
114
115 });
116
117The `.query()` method accepts strings as well:
118
119 request
120 .get('/querystring')
121 .query('search=Manny&range=1..5')
122 .then(res => {
123
124 });
125
126Or joined:
127
128 request
129 .get('/querystring')
130 .query('search=Manny')
131 .query('range=1..5')
132 .then(res => {
133
134 });
135
136## `HEAD` requests
137
138You can also use the `.query()` method for HEAD requests. The following will produce the path `/users?email=joe@smith.com`.
139
140 request
141 .head('/users')
142 .query({ email: 'joe@smith.com' })
143 .then(res => {
144
145 });
146
147## `POST` / `PUT` requests
148
149A typical JSON __POST__ request might look a little like the following, where we set the Content-Type header field appropriately, and "write" some data, in this case just a JSON string.
150
151 request.post('/user')
152 .set('Content-Type', 'application/json')
153 .send('{"name":"tj","pet":"tobi"}')
154 .then(callback)
155 .catch(errorCallback)
156
157Since JSON is undoubtedly the most common, it's the _default_! The following example is equivalent to the previous.
158
159 request.post('/user')
160 .send({ name: 'tj', pet: 'tobi' })
161 .then(callback, errorCallback)
162
163Or using multiple `.send()` calls:
164
165 request.post('/user')
166 .send({ name: 'tj' })
167 .send({ pet: 'tobi' })
168 .then(callback, errorCallback)
169
170By default sending strings will set the `Content-Type` to `application/x-www-form-urlencoded`,
171 multiple calls will be concatenated with `&`, here resulting in `name=tj&pet=tobi`:
172
173 request.post('/user')
174 .send('name=tj')
175 .send('pet=tobi')
176 .then(callback, errorCallback);
177
178SuperAgent formats are extensible, however by default "json" and "form" are supported. To send the data as `application/x-www-form-urlencoded` simply invoke `.type()` with "form", where the default is "json". This request will __POST__ the body "name=tj&pet=tobi".
179
180 request.post('/user')
181 .type('form')
182 .send({ name: 'tj' })
183 .send({ pet: 'tobi' })
184 .then(callback, errorCallback)
185
186Sending a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) object is also supported. The following example will __POST__ the content of the HTML form identified by id="myForm":
187
188 request.post('/user')
189 .send(new FormData(document.getElementById('myForm')))
190 .then(callback, errorCallback)
191
192## Setting the `Content-Type`
193
194The obvious solution is to use the `.set()` method:
195
196 request.post('/user')
197 .set('Content-Type', 'application/json')
198
199As a short-hand the `.type()` method is also available, accepting
200the canonicalized MIME type name complete with type/subtype, or
201simply the extension name such as "xml", "json", "png", etc:
202
203 request.post('/user')
204 .type('application/json')
205
206 request.post('/user')
207 .type('json')
208
209 request.post('/user')
210 .type('png')
211
212## Serializing request body
213
214SuperAgent will automatically serialize JSON and forms.
215You can setup automatic serialization for other types as well:
216
217```js
218request.serialize['application/xml'] = function (obj) {
219 return 'string generated from obj';
220};
221
222// going forward, all requests with a Content-type of
223// 'application/xml' will be automatically serialized
224```
225If you want to send the payload in a custom format, you can replace
226the built-in serialization with the `.serialize()` method on a per-request basis:
227
228```js
229request
230 .post('/user')
231 .send({foo: 'bar'})
232 .serialize(obj => {
233 return 'string generated from obj';
234 });
235```
236## Retrying requests
237
238When given the `.retry()` method, SuperAgent will automatically retry requests, if they fail in a way that is transient or could be due to a flaky Internet connection.
239
240This method has two optional arguments: number of retries (default 1) and a callback. It calls `callback(err, res)` before each retry. The callback may return `true`/`false` to control whether the request should be retried (but the maximum number of retries is always applied).
241
242 request
243 .get('https://example.com/search')
244 .retry(2) // or:
245 .retry(2, callback)
246 .then(finished);
247 .catch(failed);
248
249Use `.retry()` only with requests that are *idempotent* (i.e. multiple requests reaching the server won't cause undesirable side effects like duplicate purchases).
250
251All request methods are tried by default (which means if you do not want POST requests to be retried, you will need to pass a custom retry callback).
252
253By default the following status codes are retried:
254
255* `408`
256* `413`
257* `429`
258* `500`
259* `502`
260* `503`
261* `504`
262* `521`
263* `522`
264* `524`
265
266By default the following error codes are retried:
267
268* `'ETIMEDOUT'`
269* `'ECONNRESET'`
270* `'EADDRINUSE'`
271* `'ECONNREFUSED'`
272* `'EPIPE'`
273* `'ENOTFOUND'`
274* `'ENETUNREACH'`
275* `'EAI_AGAIN'`
276
277## Setting Accept
278
279In a similar fashion to the `.type()` method it is also possible to set the `Accept` header via the short hand method `.accept()`. Which references `request.types` as well allowing you to specify either the full canonicalized MIME type name as `type/subtype`, or the extension suffix form as "xml", "json", "png", etc. for convenience:
280
281 request.get('/user')
282 .accept('application/json')
283
284 request.get('/user')
285 .accept('json')
286
287 request.post('/user')
288 .accept('png')
289
290### Facebook and Accept JSON
291
292If you are calling Facebook's API, be sure to send an `Accept: application/json` header in your request. If you don't do this, Facebook will respond with `Content-Type: text/javascript; charset=UTF-8`, which SuperAgent will not parse and thus `res.body` will be undefined. You can do this with either `req.accept('json')` or `req.header('Accept', 'application/json')`. See [issue 1078](https://github.com/visionmedia/superagent/issues/1078) for details.
293
294## Query strings
295
296 `req.query(obj)` is a method which may be used to build up a query-string. For example populating `?format=json&dest=/login` on a __POST__:
297
298 request
299 .post('/')
300 .query({ format: 'json' })
301 .query({ dest: '/login' })
302 .send({ post: 'data', here: 'wahoo' })
303 .then(callback);
304
305By default the query string is not assembled in any particular order. An asciibetically-sorted query string can be enabled with `req.sortQuery()`. You may also provide a custom sorting comparison function with `req.sortQuery(myComparisonFn)`. The comparison function should take 2 arguments and return a negative/zero/positive integer.
306
307```js
308 // default order
309 request.get('/user')
310 .query('name=Nick')
311 .query('search=Manny')
312 .sortQuery()
313 .then(callback)
314
315 // customized sort function
316 request.get('/user')
317 .query('name=Nick')
318 .query('search=Manny')
319 .sortQuery((a, b) => a.length - b.length)
320 .then(callback)
321```
322
323## TLS options
324
325In Node.js SuperAgent supports methods to configure HTTPS requests:
326
327- `.ca()`: Set the CA certificate(s) to trust
328- `.cert()`: Set the client certificate chain(s)
329- `.key()`: Set the client private key(s)
330- `.pfx()`: Set the client PFX or PKCS12 encoded private key and certificate chain
331- `.disableTLSCerts()`: Does not reject expired or invalid TLS certs. Sets internally `rejectUnauthorized=true`. *Be warned, this method allows MITM attacks.*
332
333For more information, see Node.js [https.request docs](https://nodejs.org/api/https.html#https_https_request_options_callback).
334
335```js
336var key = fs.readFileSync('key.pem'),
337 cert = fs.readFileSync('cert.pem');
338
339request
340 .post('/client-auth')
341 .key(key)
342 .cert(cert)
343 .then(callback);
344```
345
346```js
347var ca = fs.readFileSync('ca.cert.pem');
348
349request
350 .post('https://localhost/private-ca-server')
351 .ca(ca)
352 .then(res => {});
353```
354
355## Parsing response bodies
356
357SuperAgent will parse known response-body data for you,
358currently supporting `application/x-www-form-urlencoded`,
359`application/json`, and `multipart/form-data`. You can setup
360automatic parsing for other response-body data as well:
361
362```js
363//browser
364request.parse['application/xml'] = function (str) {
365 return {'object': 'parsed from str'};
366};
367
368//node
369request.parse['application/xml'] = function (res, cb) {
370 //parse response text and set res.body here
371
372 cb(null, res);
373};
374
375//going forward, responses of type 'application/xml'
376//will be parsed automatically
377```
378
379You can set a custom parser (that takes precedence over built-in parsers) with the `.buffer(true).parse(fn)` method. If response buffering is not enabled (`.buffer(false)`) then the `response` event will be emitted without waiting for the body parser to finish, so `response.body` won't be available.
380
381### JSON / Urlencoded
382
383The property `res.body` is the parsed object, for example if a request responded with the JSON string '{"user":{"name":"tobi"}}', `res.body.user.name` would be "tobi". Likewise the x-www-form-urlencoded value of "user[name]=tobi" would yield the same result. Only one level of nesting is supported. If you need more complex data, send JSON instead.
384
385Arrays are sent by repeating the key. `.send({color: ['red','blue']})` sends `color=red&color=blue`. If you want the array keys to contain `[]` in their name, you must add it yourself, as SuperAgent doesn't add it automatically.
386
387### Multipart
388
389The Node client supports _multipart/form-data_ via the [Formidable](https://github.com/felixge/node-formidable) module. When parsing multipart responses, the object `res.files` is also available to you. Suppose for example a request responds with the following multipart body:
390
391 --whoop
392 Content-Disposition: attachment; name="image"; filename="tobi.png"
393 Content-Type: image/png
394
395 ... data here ...
396 --whoop
397 Content-Disposition: form-data; name="name"
398 Content-Type: text/plain
399
400 Tobi
401 --whoop--
402
403You would have the values `res.body.name` provided as "Tobi", and `res.files.image` as a `File` object containing the path on disk, filename, and other properties.
404
405### Binary
406
407In browsers, you may use `.responseType('blob')` to request handling of binary response bodies. This API is unnecessary when running in node.js. The supported argument values for this method are
408
409- `'blob'` passed through to the XmlHTTPRequest `responseType` property
410- `'arraybuffer'` passed through to the XmlHTTPRequest `responseType` property
411
412```js
413req.get('/binary.data')
414 .responseType('blob')
415 .then(res => {
416 // res.body will be a browser native Blob type here
417 });
418```
419
420For more information, see the Mozilla Developer Network [xhr.responseType docs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType).
421
422## Response properties
423
424Many helpful flags and properties are set on the `Response` object, ranging from the response text, parsed response body, header fields, status flags and more.
425
426### Response text
427
428The `res.text` property contains the unparsed response body string. This property is always present for the client API, and only when the mime type matches "text/*", "*/json", or "x-www-form-urlencoded" by default for node. The reasoning is to conserve memory, as buffering text of large bodies such as multipart files or images is extremely inefficient. To force buffering see the "Buffering responses" section.
429
430### Response body
431
432Much like SuperAgent can auto-serialize request data, it can also automatically parse it. When a parser is defined for the Content-Type, it is parsed, which by default includes "application/json" and "application/x-www-form-urlencoded". The parsed object is then available via `res.body`.
433
434### Response header fields
435
436The `res.header` contains an object of parsed header fields, lowercasing field names much like node does. For example `res.header['content-length']`.
437
438### Response Content-Type
439
440The Content-Type response header is special-cased, providing `res.type`, which is void of the charset (if any). For example the Content-Type of "text/html; charset=utf8" will provide "text/html" as `res.type`, and the `res.charset` property would then contain "utf8".
441
442### Response status
443
444The response status flags help determine if the request was a success, among other useful information, making SuperAgent ideal for interacting with RESTful web services. These flags are currently defined as:
445
446 var type = status / 100 | 0;
447
448 // status / class
449 res.status = status;
450 res.statusType = type;
451
452 // basics
453 res.info = 1 == type;
454 res.ok = 2 == type;
455 res.clientError = 4 == type;
456 res.serverError = 5 == type;
457 res.error = 4 == type || 5 == type;
458
459 // sugar
460 res.accepted = 202 == status;
461 res.noContent = 204 == status || 1223 == status;
462 res.badRequest = 400 == status;
463 res.unauthorized = 401 == status;
464 res.notAcceptable = 406 == status;
465 res.notFound = 404 == status;
466 res.forbidden = 403 == status;
467
468## Aborting requests
469
470To abort requests simply invoke the `req.abort()` method.
471
472## Timeouts
473
474Sometimes networks and servers get "stuck" and never respond after accepting a request. Set timeouts to avoid requests waiting forever.
475
476 * `req.timeout({deadline:ms})` or `req.timeout(ms)` (where `ms` is a number of milliseconds > 0) sets a deadline for the entire request (including all uploads, redirects, server processing time) to complete. If the response isn't fully downloaded within that time, the request will be aborted.
477
478 * `req.timeout({response:ms})` sets maximum time to wait for the first byte to arrive from the server, but it does not limit how long the entire download can take. Response timeout should be at least few seconds longer than just the time it takes the server to respond, because it also includes time to make DNS lookup, TCP/IP and TLS connections, and time to upload request data.
479
480You should use both `deadline` and `response` timeouts. This way you can use a short response timeout to detect unresponsive networks quickly, and a long deadline to give time for downloads on slow, but reliable, networks. Note that both of these timers limit how long *uploads* of attached files are allowed to take. Use long timeouts if you're uploading files.
481
482 request
483 .get('/big-file?network=slow')
484 .timeout({
485 response: 5000, // Wait 5 seconds for the server to start sending,
486 deadline: 60000, // but allow 1 minute for the file to finish loading.
487 })
488 .then(res => {
489 /* responded in time */
490 }, err => {
491 if (err.timeout) { /* timed out! */ } else { /* other error */ }
492 });
493
494Timeout errors have a `.timeout` property.
495
496## Authentication
497
498In both Node and browsers auth available via the `.auth()` method:
499
500 request
501 .get('http://local')
502 .auth('tobi', 'learnboost')
503 .then(callback);
504
505
506In the _Node_ client Basic auth can be in the URL as "user:pass":
507
508 request.get('http://tobi:learnboost@local').then(callback);
509
510By default only `Basic` auth is used. In browser you can add `{type:'auto'}` to enable all methods built-in in the browser (Digest, NTLM, etc.):
511
512 request.auth('digest', 'secret', {type:'auto'})
513
514The `auth` method also supports a `type` of `bearer`, to specify token-based authentication:
515
516 request.auth('my_token', { type: 'bearer' })
517
518## Following redirects
519
520By default up to 5 redirects will be followed, however you may specify this with the `res.redirects(n)` method:
521
522 const response = await request.get('/some.png').redirects(2);
523
524Redirects exceeding the limit are treated as errors. Use `.ok(res => res.status < 400)` to read them as successful responses.
525
526## Agents for global state
527
528### Saving cookies
529
530In Node SuperAgent does not save cookies by default, but you can use the `.agent()` method to create a copy of SuperAgent that saves cookies. Each copy has a separate cookie jar.
531
532 const agent = request.agent();
533 agent
534 .post('/login')
535 .then(() => {
536 return agent.get('/cookied-page');
537 });
538
539In browsers cookies are managed automatically by the browser, so the `.agent()` does not isolate cookies.
540
541### Default options for multiple requests
542
543Regular request methods called on the agent will be used as defaults for all requests made by that agent.
544
545 const agent = request.agent()
546 .use(plugin)
547 .auth(shared);
548
549 await agent.get('/with-plugin-and-auth');
550 await agent.get('/also-with-plugin-and-auth');
551
552The complete list of methods that the agent can use to set defaults is: `use`, `on`, `once`, `set`, `query`, `type`, `accept`, `auth`, `withCredentials`, `sortQuery`, `retry`, `ok`, `redirects`, `timeout`, `buffer`, `serialize`, `parse`, `ca`, `key`, `pfx`, `cert`.
553
554## Piping data
555
556The Node client allows you to pipe data to and from the request. Please note that `.pipe()` is used **instead of** `.end()`/`.then()` methods.
557
558For example piping a file's contents as the request:
559
560 const request = require('superagent');
561 const fs = require('fs');
562
563 const stream = fs.createReadStream('path/to/my.json');
564 const req = request.post('/somewhere');
565 req.type('json');
566 stream.pipe(req);
567
568Note that when you pipe to a request, superagent sends the piped data with [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding), which isn't supported by all servers (for instance, Python WSGI servers).
569
570Or piping the response to a file:
571
572 const stream = fs.createWriteStream('path/to/my.json');
573 const req = request.get('/some.json');
574 req.pipe(stream);
575
576 It's not possible to mix pipes and callbacks or promises. Note that you should **NOT** attempt to pipe the result of `.end()` or the `Response` object:
577
578 // Don't do either of these:
579 const stream = getAWritableStream();
580 const req = request
581 .get('/some.json')
582 // BAD: this pipes garbage to the stream and fails in unexpected ways
583 .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
584 const req = request
585 .get('/some.json')
586 .end()
587 // BAD: this is also unsupported, .pipe calls .end for you.
588 .pipe(nope_its_too_late);
589
590In a [future version](https://github.com/visionmedia/superagent/issues/1188) of superagent, improper calls to `pipe()` will fail.
591
592## Multipart requests
593
594SuperAgent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
595
596When you use `.field()` or `.attach()` you can't use `.send()` and you *must not* set `Content-Type` (the correct type will be set for you).
597
598### Attaching files
599
600To send a file use `.attach(name, [file], [options])`. You can attach multiple files by calling `.attach` multiple times. The arguments are:
601
602 * `name` — field name in the form.
603 * `file` — either string with file path or `Blob`/`Buffer` object.
604 * `options` — (optional) either string with custom file name or `{filename: string}` object. In Node also `{contentType: 'mime/type'}` is supported. In browser create a `Blob` with an appropriate type instead.
605
606<br>
607
608 request
609 .post('/upload')
610 .attach('image1', 'path/to/felix.jpeg')
611 .attach('image2', imageBuffer, 'luna.jpeg')
612 .field('caption', 'My cats')
613 .then(callback);
614
615### Field values
616
617Much like form fields in HTML, you can set field values with `.field(name, value)` and `.field({name: value})`. Suppose you want to upload a few images with your name and email, your request might look something like this:
618
619 request
620 .post('/upload')
621 .field('user[name]', 'Tobi')
622 .field('user[email]', 'tobi@learnboost.com')
623 .field('friends[]', ['loki', 'jane'])
624 .attach('image', 'path/to/tobi.png')
625 .then(callback);
626
627## Compression
628
629The node client supports compressed responses, best of all, you don't have to do anything! It just works.
630
631## Buffering responses
632
633To force buffering of response bodies as `res.text` you may invoke `req.buffer()`. To undo the default of buffering for text responses such as "text/plain", "text/html" etc you may invoke `req.buffer(false)`.
634
635When buffered the `res.buffered` flag is provided, you may use this to handle both buffered and unbuffered responses in the same callback.
636
637## CORS
638
639For security reasons, browsers will block cross-origin requests unless the server opts-in using CORS headers. Browsers will also make extra __OPTIONS__ requests to check what HTTP headers and methods are allowed by the server. [Read more about CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).
640
641The `.withCredentials()` method enables the ability to send cookies from the origin, however only when `Access-Control-Allow-Origin` is _not_ a wildcard ("*"), and `Access-Control-Allow-Credentials` is "true".
642
643 request
644 .get('https://api.example.com:4001/')
645 .withCredentials()
646 .then(res => {
647 assert.equal(200, res.status);
648 assert.equal('tobi', res.text);
649 })
650
651## Error handling
652
653Your callback function will always be passed two arguments: error and response. If no error occurred, the first argument will be null:
654
655 request
656 .post('/upload')
657 .attach('image', 'path/to/tobi.png')
658 .then(res => {
659
660 });
661
662An "error" event is also emitted, with you can listen for:
663
664 request
665 .post('/upload')
666 .attach('image', 'path/to/tobi.png')
667 .on('error', handle)
668 .then(res => {
669
670 });
671
672Note that **superagent considers 4xx and 5xx responses (as well as unhandled 3xx responses) errors by default**. For example, if you get a `304 Not modified`, `403 Forbidden` or `500 Internal server error` response, this status information will be available via `err.status`. Errors from such responses also contain an `err.response` field with all of the properties mentioned in "[Response properties](#response-properties)". The library behaves in this way to handle the common case of wanting success responses and treating HTTP error status codes as errors while still allowing for custom logic around specific error conditions.
673
674Network failures, timeouts, and other errors that produce no response will contain no `err.status` or `err.response` fields.
675
676If you wish to handle 404 or other HTTP error responses, you can query the `err.status` property. When an HTTP error occurs (4xx or 5xx response) the `res.error` property is an `Error` object, this allows you to perform checks such as:
677
678 if (err && err.status === 404) {
679 alert('oh no ' + res.body.message);
680 }
681 else if (err) {
682 // all other error types we handle generically
683 }
684
685Alternatively, you can use the `.ok(callback)` method to decide whether a response is an error or not. The callback to the `ok` function gets a response and returns `true` if the response should be interpreted as success.
686
687 request.get('/404')
688 .ok(res => res.status < 500)
689 .then(response => {
690 // reads 404 page as a successful response
691 })
692
693## Progress tracking
694
695SuperAgent fires `progress` events on upload and download of large files.
696
697 request.post(url)
698 .attach('field_name', file)
699 .on('progress', event => {
700 /* the event is:
701 {
702 direction: "upload" or "download"
703 percent: 0 to 100 // may be missing if file size is unknown
704 total: // total file size, may be missing
705 loaded: // bytes downloaded or uploaded so far
706 } */
707 })
708 .then()
709
710
711## Testing on localhost
712
713### Forcing specific connection IP address
714
715In Node.js it's possible to ignore DNS resolution and direct all requests to a specific IP address using `.connect()` method. For example, this request will go to localhost instead of `example.com`:
716
717 const res = await request.get("http://example.com").connect("127.0.0.1");
718
719Because the request may be redirected, it's possible to specify multiple hostnames and multiple IPs, as well as a special `*` as the fallback (note: other wildcards are not supported). The requests will keep their `Host` header with the original value. `.connect(undefined)` turns off the feature.
720
721 const res = await request.get("http://redir.example.com:555")
722 .connect({
723 "redir.example.com": "127.0.0.1", // redir.example.com:555 will use 127.0.0.1:555
724 "www.example.com": false, // don't override this one; use DNS as normal
725 "mapped.example.com": { host: "127.0.0.1", port: 8080}, // mapped.example.com:* will use 127.0.0.1:8080
726 "*": "proxy.example.com", // all other requests will go to this host
727 });
728
729### Ignoring broken/insecure HTTPS on localhost
730
731In Node.js, when HTTPS is misconfigured and insecure (e.g. using self-signed certificate *without* specifying own `.ca()`), it's still possible to permit requests to `localhost` by calling `.trustLocalhost()`:
732
733 const res = await request.get("https://localhost").trustLocalhost()
734
735Together with `.connect("127.0.0.1")` this may be used to force HTTPS requests to any domain to be re-routed to `localhost` instead.
736
737It's generally safe to ignore broken HTTPS on `localhost`, because the loopback interface is not exposed to untrusted networks. Trusting `localhost` may become the default in the future. Use `.trustLocalhost(false)` to force check of `127.0.0.1`'s authenticity.
738
739We intentionally don't support disabling of HTTPS security when making requests to any other IP, because such options end up abused as a quick "fix" for HTTPS problems. You can get free HTTPS certificates from [Let's Encrypt](https://certbot.eff.org) or set your own CA (`.ca(ca_public_pem)`) to make your self-signed certificates trusted.
740
741## Promise and Generator support
742
743SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and the `async`/`await` syntax.
744
745 const res = await request.get(url);
746
747If you're using promises, **do not** call `.end()` or `.pipe()`. Any use of `.then()` or `await` disables all other ways of using the request.
748
749Libraries like [co](https://github.com/tj/co) or a web framework like [koa](https://github.com/koajs/koa) can `yield` on any SuperAgent method:
750
751 const req = request
752 .get('http://local')
753 .auth('tobi', 'learnboost');
754 const res = yield req;
755
756Note that SuperAgent expects the global `Promise` object to be present. You'll need a polyfill to use promises in Internet Explorer or Node.js 0.10.
757
758## Browser and node versions
759
760SuperAgent has two implementations: one for web browsers (using XHR) and one for Node.JS (using core http module). By default Browserify and WebPack will pick the browser version.
761
762If want to use WebPack to compile code for Node.JS, you *must* specify [node target](https://webpack.github.io/docs/configuration.html#target) in its configuration.
763
764### Using browser version in electron
765
766[Electron](https://electron.atom.io/) developers report if you would prefer to use the browser version of SuperAgent instead of the Node version, you can `require('superagent/superagent')`. Your requests will now show up in the Chrome developer tools Network tab. Note this environment is not covered by automated test suite and not officially supported.