UNPKG

24 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 .end(function(err, res){
12 if (err || !res.ok) {
13 alert('Oh no! error');
14 } else {
15 alert('yay got ' + JSON.stringify(res.body));
16 }
17 });
18
19## Test documentation
20
21The following [test documentation](docs/test.html) was generated with [Mocha's](http://mochajs.org/) "doc" reporter, and directly reflects the test suite. This provides an additional source of documentation.
22
23## Request basics
24
25A request can be initiated by invoking the appropriate method on the `request` object, then calling `.end()` to send the request. For example a simple __GET__ request:
26
27 request
28 .get('/search')
29 .end(function(err, res){
30
31 });
32
33A method string may also be passed:
34
35 request('GET', '/search').end(callback);
36
37ES6 promises are supported. *Instead* of `.end()` you can call `.then()`:
38
39 request('GET', '/search').then(success, failure);
40
41The __Node__ client may also provide absolute URLs. In browsers absolute URLs won't work unless the server implements [CORS](#cors).
42
43 request
44 .get('http://example.com/search')
45 .end(function(err, res){
46
47 });
48
49The __Node__ client supports making requests to [Unix Domain Sockets](http://en.wikipedia.org/wiki/Unix_domain_socket):
50
51 // pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
52 // Use `%2F` as `/` in SOCKET_PATH
53 request
54 .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search')
55 .end(function(err, res){
56
57 });
58
59__DELETE__, __HEAD__, __PATCH__, __POST__, and __PUT__ requests can also be used, simply change the method name:
60
61 request
62 .head('/favicon.ico')
63 .end(function(err, res){
64
65 });
66
67__DELETE__ can be also called as `.del()` for compatibility with old IE where `delete` is a reserved word.
68
69 The HTTP method defaults to __GET__, so if you wish, the following is valid:
70
71 request('/search', function(err, res){
72
73 });
74
75## Setting header fields
76
77Setting header fields is simple, invoke `.set()` with a field name and value:
78
79 request
80 .get('/search')
81 .set('API-Key', 'foobar')
82 .set('Accept', 'application/json')
83 .end(callback);
84
85You may also pass an object to set several fields in a single call:
86
87 request
88 .get('/search')
89 .set({ 'API-Key': 'foobar', Accept: 'application/json' })
90 .end(callback);
91
92## `GET` requests
93
94The `.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`.
95
96 request
97 .get('/search')
98 .query({ query: 'Manny' })
99 .query({ range: '1..5' })
100 .query({ order: 'desc' })
101 .end(function(err, res){
102
103 });
104
105Or as a single object:
106
107 request
108 .get('/search')
109 .query({ query: 'Manny', range: '1..5', order: 'desc' })
110 .end(function(err, res){
111
112 });
113
114The `.query()` method accepts strings as well:
115
116 request
117 .get('/querystring')
118 .query('search=Manny&range=1..5')
119 .end(function(err, res){
120
121 });
122
123Or joined:
124
125 request
126 .get('/querystring')
127 .query('search=Manny')
128 .query('range=1..5')
129 .end(function(err, res){
130
131 });
132
133## `HEAD` requests
134
135You can also use the `.query()` method for HEAD requests. The following will produce the path `/users?email=joe@smith.com`.
136
137 request
138 .head('/users')
139 .query({ email: 'joe@smith.com' })
140 .end(function(err, res){
141
142 });
143
144## `POST` / `PUT` requests
145
146A 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.
147
148 request.post('/user')
149 .set('Content-Type', 'application/json')
150 .send('{"name":"tj","pet":"tobi"}')
151 .end(callback)
152
153Since JSON is undoubtedly the most common, it's the _default_! The following example is equivalent to the previous.
154
155 request.post('/user')
156 .send({ name: 'tj', pet: 'tobi' })
157 .end(callback)
158
159Or using multiple `.send()` calls:
160
161 request.post('/user')
162 .send({ name: 'tj' })
163 .send({ pet: 'tobi' })
164 .end(callback)
165
166By default sending strings will set the `Content-Type` to `application/x-www-form-urlencoded`,
167 multiple calls will be concatenated with `&`, here resulting in `name=tj&pet=tobi`:
168
169 request.post('/user')
170 .send('name=tj')
171 .send('pet=tobi')
172 .end(callback);
173
174SuperAgent 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".
175
176 request.post('/user')
177 .type('form')
178 .send({ name: 'tj' })
179 .send({ pet: 'tobi' })
180 .end(callback)
181
182Sending 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":
183
184 request.post('/user')
185 .send(new FormData(document.getElementById('myForm')))
186 .end(callback)
187
188## Setting the `Content-Type`
189
190The obvious solution is to use the `.set()` method:
191
192 request.post('/user')
193 .set('Content-Type', 'application/json')
194
195As a short-hand the `.type()` method is also available, accepting
196the canonicalized MIME type name complete with type/subtype, or
197simply the extension name such as "xml", "json", "png", etc:
198
199 request.post('/user')
200 .type('application/json')
201
202 request.post('/user')
203 .type('json')
204
205 request.post('/user')
206 .type('png')
207
208## Serializing request body
209
210SuperAgent will automatically serialize JSON and forms. If you want to send the payload in a custom format, you can replace the built-in serialization with `.serialize()` method.
211
212## Retrying requests
213
214When 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. `.retry()` takes an optional argument which is the maximum number of times to retry failed requests; the default is 3 times.
215
216 request
217 .get('http://example.com/search')
218 .retry(2)
219 .end(callback);
220
221## Setting Accept
222
223In 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:
224
225 request.get('/user')
226 .accept('application/json')
227
228 request.get('/user')
229 .accept('json')
230
231 request.post('/user')
232 .accept('png')
233
234### Facebook and Accept JSON
235
236If 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.
237
238## Query strings
239
240 `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__:
241
242 request
243 .post('/')
244 .query({ format: 'json' })
245 .query({ dest: '/login' })
246 .send({ post: 'data', here: 'wahoo' })
247 .end(callback);
248
249By 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.
250
251```js
252 // default order
253 request.get('/user')
254 .query('name=Nick')
255 .query('search=Manny')
256 .sortQuery()
257 .end(callback)
258
259 // customized sort function
260 request.get('/user')
261 .query('name=Nick')
262 .query('search=Manny')
263 .sortQuery(function(a, b){
264 return a.length - b.length;
265 })
266 .end(callback)
267```
268
269## TLS options
270
271In Node.js SuperAgent supports methods to configure HTTPS requests:
272
273- `.ca()`: Set the CA certificate(s) to trust
274- `.cert()`: Set the client certificate chain(s)
275- `.key()`: Set the client private key(s)
276- `.pfx()`: Set the client PFX or PKCS12 encoded private key and certificate chain
277
278For more information, see Node.js [https.request docs](https://nodejs.org/api/https.html#https_https_request_options_callback).
279
280```js
281var key = fs.readFileSync('key.pem'),
282 cert = fs.readFileSync('cert.pem');
283
284request
285 .post('/client-auth')
286 .key(key)
287 .cert(cert)
288 .end(callback);
289```
290
291```js
292var ca = fs.readFileSync('ca.cert.pem');
293
294request
295 .post('https://localhost/private-ca-server')
296 .ca(ca)
297 .end(callback);
298```
299
300## Parsing response bodies
301
302SuperAgent will parse known response-body data for you, currently supporting `application/x-www-form-urlencoded`, `application/json`, and `multipart/form-data`.
303
304You 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.
305
306### JSON / Urlencoded
307
308The 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.
309
310Arrays 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.
311
312### Multipart
313
314The 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:
315
316 --whoop
317 Content-Disposition: attachment; name="image"; filename="tobi.png"
318 Content-Type: image/png
319
320 ... data here ...
321 --whoop
322 Content-Disposition: form-data; name="name"
323 Content-Type: text/plain
324
325 Tobi
326 --whoop--
327
328You 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.
329
330### Binary
331
332In 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
333
334- `'blob'` passed through to the XmlHTTPRequest `responseType` property
335- `'arraybuffer'` passed through to the XmlHTTPRequest `responseType` property
336
337```js
338req.get('/binary.data')
339 .responseType('blob')
340 .end(function (error, res) {
341 // res.body will be a browser native Blob type here
342 });
343```
344
345For more information, see the Mozilla Developer Network [xhr.responseType docs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType).
346
347## Response properties
348
349Many helpful flags and properties are set on the `Response` object, ranging from the response text, parsed response body, header fields, status flags and more.
350
351### Response text
352
353The `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.
354
355### Response body
356
357Much 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`.
358
359### Response header fields
360
361The `res.header` contains an object of parsed header fields, lowercasing field names much like node does. For example `res.header['content-length']`.
362
363### Response Content-Type
364
365The 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".
366
367### Response status
368
369The 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:
370
371 var type = status / 100 | 0;
372
373 // status / class
374 res.status = status;
375 res.statusType = type;
376
377 // basics
378 res.info = 1 == type;
379 res.ok = 2 == type;
380 res.clientError = 4 == type;
381 res.serverError = 5 == type;
382 res.error = 4 == type || 5 == type;
383
384 // sugar
385 res.accepted = 202 == status;
386 res.noContent = 204 == status || 1223 == status;
387 res.badRequest = 400 == status;
388 res.unauthorized = 401 == status;
389 res.notAcceptable = 406 == status;
390 res.notFound = 404 == status;
391 res.forbidden = 403 == status;
392
393## Aborting requests
394
395To abort requests simply invoke the `req.abort()` method.
396
397## Timeouts
398
399Sometimes networks and servers get "stuck" and never respond after accepting a request. Set timeouts to avoid requests waiting forever.
400
401 * `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 redirects) to complete. If the response isn't fully downloaded within that time, the request will be aborted.
402
403 * `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 a few seconds longer than just the time it takes server to respond, because it also includes time to make DNS lookup, TCP/IP and TLS connections.
404
405You 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.
406
407 request
408 .get('/big-file?network=slow')
409 .timeout({
410 response: 5000, // Wait 5 seconds for the server to start sending,
411 deadline: 60000, // but allow 1 minute for the file to finish loading.
412 })
413 .end(function(err, res){
414 if (err.timeout) { /* timed out! */ }
415 });
416
417Timeout errors have a `.timeout` property.
418
419## Authentication
420
421In both Node and browsers auth available via the `.auth()` method:
422
423 request
424 .get('http://local')
425 .auth('tobi', 'learnboost')
426 .end(callback);
427
428
429In the _Node_ client Basic auth can be in the URL as "user:pass":
430
431 request.get('http://tobi:learnboost@local').end(callback);
432
433By 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.):
434
435 request.auth('digest', 'secret', {type:'auto'})
436
437## Following redirects
438
439By default up to 5 redirects will be followed, however you may specify this with the `res.redirects(n)` method:
440
441 request
442 .get('/some.png')
443 .redirects(2)
444 .end(callback);
445
446## Preserving cookies
447
448In 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.
449
450 const agent = request.agent();
451 agent
452 .post('/login')
453 .then(() => {
454 return agent.get('/cookied-page');
455 });
456
457In browsers cookies are managed automatically by the browser, and there is no `.agent()` method.
458
459## Piping data
460
461The Node client allows you to pipe data to and from the request. For example piping a file's contents as the request:
462
463 const request = require('superagent');
464 const fs = require('fs');
465
466 const stream = fs.createReadStream('path/to/my.json');
467 const req = request.post('/somewhere');
468 req.type('json');
469 stream.pipe(req);
470
471Note 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).
472
473Or piping the response to a file:
474
475 const stream = fs.createWriteStream('path/to/my.json');
476 const req = request.get('/some.json');
477 req.pipe(stream);
478
479## Multipart requests
480
481SuperAgent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
482
483When 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).
484
485### Attaching files
486
487To send a file use `.attach(name, [file], [options])`. You can attach multiple files by calling `.attach` multiple times. The arguments are:
488
489 * `name` — filed name in the form.
490 * `file` — either string with file path or `Blob`/`Buffer` object.
491 * `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.
492
493<br>
494
495 request
496 .post('/upload')
497 .attach('image1', 'path/to/felix.jpeg')
498 .attach('image2', imageBuffer, 'luna.jpeg')
499 .field('caption', 'My cats')
500 .end(callback);
501
502### Field values
503
504Much 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:
505
506 request
507 .post('/upload')
508 .field('user[name]', 'Tobi')
509 .field('user[email]', 'tobi@learnboost.com')
510 .field('friends[]', ['loki', 'jane'])
511 .attach('image', 'path/to/tobi.png')
512 .end(callback);
513
514## Compression
515
516The node client supports compressed responses, best of all, you don't have to do anything! It just works.
517
518## Buffering responses
519
520To 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)`.
521
522When buffered the `res.buffered` flag is provided, you may use this to handle both buffered and unbuffered responses in the same callback.
523
524## CORS
525
526For 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).
527
528The `.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".
529
530 request
531 .get('http://api.example.com:4001/')
532 .withCredentials()
533 .then(function(res){
534 assert.equal(200, res.status);
535 assert.equal('tobi', res.text);
536 })
537
538## Error handling
539
540Your callback function will always be passed two arguments: error and response. If no error occurred, the first argument will be null:
541
542 request
543 .post('/upload')
544 .attach('image', 'path/to/tobi.png')
545 .end(function(err, res){
546
547 });
548
549An "error" event is also emitted, with you can listen for:
550
551 request
552 .post('/upload')
553 .attach('image', 'path/to/tobi.png')
554 .on('error', handle)
555 .end(function(err, res){
556
557 });
558
559Note that a 4xx or 5xx response with super agent **are** considered an error by default. For example if you get a 500 or 403 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.
560
561Network failures, timeouts, and other errors that produce no response will contain no `err.status` or `err.response` fields.
562
563If 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:
564
565 if (err && err.status === 404) {
566 alert('oh no ' + res.body.message);
567 }
568 else if (err) {
569 // all other error types we handle generically
570 }
571
572Alternatively, 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.
573
574 request.get('/404')
575 .ok(res => res.status < 500)
576 .then(response => {
577 // reads 404 page as a successful response
578 })
579
580## Progress tracking
581
582SuperAgent fires `progress` events on upload and download of large files.
583
584 request.post(url)
585 .attach('field_name', file)
586 .on('progress', event => {
587 /* the event is:
588 {
589 direction: "upload" or "download"
590 percent: 0 to 100 // may be missing if file size is unknown
591 total: // total file size, may be missing
592 loaded: // bytes downloaded or uploaded so far
593 } */
594 })
595 .end()
596
597## Promise and Generator support
598
599SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax. Do not call `.end()` if you're using promises.
600
601Libraries like [co](https://github.com/tj/co) or a web framework like [koa](https://github.com/koajs/koa) can `yield` on any SuperAgent method:
602
603 const req = request
604 .get('http://local')
605 .auth('tobi', 'learnboost');
606 const res = yield req;
607
608Note 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.
609
610## Browser and node versions
611
612SuperAgent 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.
613
614If 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.
615
616### Using browser version in electron
617
618[Electron](http://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.