restify-new-nodejs-compatible
Version:
REST framework
420 lines (325 loc) • 11.3 kB
Markdown
---
title: Client Guide
permalink: /docs/client-guide/
---
There are actually three separate clients shipped in restify:
* **JsonClient:** sends and expects application/json
* **StringClient:** sends url-encoded request and expects text/plain
* **HttpClient:** thin wrapper over node's http/https libraries
The idea being that if you want to support "typical" control-plane
REST APIs, you probably want the `JsonClient`, or if you're using some
other serialization (like XML) you'd write your own client that
extends the `StringClient`. If you need streaming support, you'll need
to do some work on top of the `HttpClient`, as `StringClient` and
friends buffer requests/responses.
All clients support retry with exponential backoff for getting a TCP
connection; they do not perform retries on 5xx error codes like
previous versions of the restify client. You can set `retry` to `false` to
disable this logic altogether. Also, all clients support a `connectTimeout`
field, which is use *on each retry*. The default is not to set a
`connectTimeout`, so you end up with the node.js socket defaults.
Here's an example of hitting the
[Joyent CloudAPI](https://apidocs.joyent.com/cloudapi/):
```js
var restify = require('restify-clients');
// Creates a JSON client
var client = restify.createJsonClient({
url: 'https://us-east-1.api.joyent.com'
});
client.basicAuth('$login', '$password');
client.get('/my/machines', function(err, req, res, obj) {
assert.ifError(err);
console.log(JSON.stringify(obj, null, 2));
});
```
As a short-hand, a client can be initialized with a string-URL rather than
an options object:
```js
var restify = require('restify-clients');
var client = restify.createJsonClient('https://us-east-1.api.joyent.com');
```
Note that all further documentation refers to the "short-hand" form of
methods like `get/put/del` which take a string path. You can also
pass in an object to any of those methods with extra params (notably
headers):
```js
var options = {
path: '/foo/bar',
headers: {
'x-foo': 'bar'
},
retry: {
'retries': 0
},
agent: false
};
client.get(options, function(err, req, res) { .. });
```
If you need to interpose additional headers in the request before it is sent on
to the server, you can provide a synchronous callback function as the
`signRequest` option when creating a client. This is particularly useful with
[node-http-signature](https://github.com/joyent/node-http-signature), which
needs to attach a cryptographic signature of selected outgoing headers. If
provided, this callback will be invoked with a single parameter: the outgoing
`http.ClientRequest` object.
## JsonClient
The JSON Client is the highest-level client bundled with restify-clients; it
exports a set of methods that map directly to HTTP verbs. All
callbacks look like `function(err, req, res, [obj])`, where `obj` is
optional, depending on if content was returned. HTTP status codes are
not interpreted, so if the server returned 4xx or something with a
JSON payload, `obj` will be the JSON payload. `err` however will be
set if the server returned a status code >= 400 (it will be one of the
restify HTTP errors). If `obj` looks like a `RestError`:
```js
{
"code": "FooError",
"message": "some foo happened"
}
```
then `err` gets "upconverted" into a `RestError` for you. Otherwise
it will be an `HttpError`.
### createJsonClient(options)
```js
var client = restify.createJsonClient({
url: 'https://api.us-east-1.joyent.com',
version: '*'
});
```
Options:
|Name|Type|Description|
|----|----|-----------|
|accept|String|Accept header to send|
|connectTimeout|Number|Amount of time to wait for a socket|
|requestTimeout|Number|Amount of time to wait for the request to finish|
|dtrace|Object|node-dtrace-provider handle|
|gzip|Object|Will compress data when sent using `content-encoding: gzip`|
|headers|Object|HTTP headers to set in all requests|
|log|Object|[pino](https://github.com/pinojs/pino) instance|
|retry|Object|options to provide to node-retry;"false" disables retry; defaults to 4 retries|
|signRequest|Function|synchronous callback for interposing headers before request is sent|
|url|String|Fully-qualified URL to connect to|
|userAgent|String|user-agent string to use; restify inserts one, but you can override it|
|version|String|semver string to set the accept-version|
### get(path, callback)
Performs an HTTP get; if no payload was returned, `obj` defaults to
`{}` for you (so you don't get a bunch of null pointer errors).
```js
client.get('/foo/bar', function(err, req, res, obj) {
assert.ifError(err);
console.log('%j', obj);
});
```
### head(path, callback)
Just like `get`, but without `obj`:
```js
client.head('/foo/bar', function(err, req, res) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
});
```
### post(path, object, callback)
Takes a complete object to serialize and send to the server.
```js
client.post('/foo', { hello: 'world' }, function(err, req, res, obj) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
console.log('%j', obj);
});
```
### put(path, object, callback)
Just like `post`:
```js
client.put('/foo', { hello: 'world' }, function(err, req, res, obj) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
console.log('%j', obj);
});
```
### del(path, callback)
`del` doesn't take content, since you know, it should't:
```js
client.del('/foo/bar', function(err, req, res) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
});
```
## StringClient
`StringClient` is what `JsonClient` is built on, and provides a base
for you to write other buffering/parsing clients (like say an XML
client). If you need to talk to some "raw" HTTP server, then
`StringClient` is what you want, as it by default will provide you
with content uploads in `application/x-www-form-url-encoded` and
downloads as `text/plain`. To extend a `StringClient`, take a look at
the source for `JsonClient`. Effectively, you extend it, and set the
appropriate options in the constructor and implement a `write` (for
put/post) and `parse` method (for all HTTP bodies), and that's it.
### createStringClient(options)
```js
var client = restify.createStringClient({
url: 'https://example.com'
})
```
### get(path, callback)
Performs an HTTP get; if no payload was returned, `data` defaults to
`''` for you (so you don't get a bunch of null pointer errors).
```js
client.get('/foo/bar', function(err, req, res, data) {
assert.ifError(err);
console.log('%s', data);
});
```
### head(path, callback)
Just like `get`, but without `data`:
```js
client.head('/foo/bar', function(err, req, res) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
});
```
### post(path, object, callback)
Takes a complete object to serialize and send to the server.
```js
client.post('/foo', { hello: 'world' }, function(err, req, res, data) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
console.log('%s', data);
});
```
### put(path, object, callback)
Just like `post`:
```js
client.put('/foo', { hello: 'world' }, function(err, req, res, data) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
console.log('%s', data);
});
```
### del(path, callback)
`del` doesn't take content, since you know, it should't:
```js
client.del('/foo/bar', function(err, req, res) {
assert.ifError(err);
console.log('%d -> %j', res.statusCode, res.headers);
});
```
## HttpClient
`HttpClient` is the lowest-level client shipped in restify, and is
basically just some sugar over the top of node's http/https modules
(with HTTP methods like the other clients). It is useful if you want
to stream with restify. Note that the event below is unfortunately
named `result` and not `response` (because
[Event 'response'](http://nodejs.org/docs/latest/api/all.html#event_response_)
is already used).
```js
client = restify.createClient({
url: 'http://127.0.0.1'
});
client.get('/str/mcavage', function(err, req) {
assert.ifError(err); // connection error
req.on('result', function(err, res) {
assert.ifError(err); // HTTP status code >= 400
res.body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
res.body += chunk;
});
res.on('end', function() {
console.log(res.body);
});
});
});
```
Or a write:
```js
client.post(opts, function(err, req) {
assert.ifError(connectErr);
req.on('result', function(err, res) {
assert.ifError(err);
res.body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
res.body += chunk;
});
res.on('end', function() {
console.log(res.body);
});
});
req.write('hello world');
req.end();
});
```
Note that get/head/del all call `req.end()` for you, so you can't
write data over those. Otherwise, all the same methods exist as
`JsonClient/StringClient`.
One wishing to extend the `HttpClient` should look at the internals
and note that `read` and `write` probably need to be overridden.
### Proxy
There are several options for enabling a proxy for the
http client. The following options are available to set a proxy url:
```js
// Set the proxy option in the client configuration
restify.createClient({
proxy: 'http://127.0.0.1'
});
```
From environment variables:
```sh
$ export HTTPS_PROXY = 'https://127.0.0.1'
$ export HTTP_PROXY = 'http://127.0.0.1'
```
There is an option to disable the use of a proxy on a url basis or for
all urls. This can be enabled by setting an environment variable.
Don't proxy requests to any urls
```sh
$ export NO_PROXY='*'
```
Don't proxy requests to localhost
```sh
$ export NO_PROXY='127.0.0.1'
```
Don't proxy requests to localhost on port 8000
```sh
$ export NO_PROXY='localhost:8000'
```
Don't proxy requests to multiple IPs
```sh
$ export NO_PROXY='127.0.0.1, 8.8.8.8'
```
**Note**: The url being requested must match the full hostname in
the proxy configuration or NO_PROXY environment variable. DNS
lookups are not performed to determine the IP address of a hostname.
### basicAuth(username, password)
Since it hasn't been mentioned yet, this convenience method (available
on all clients), just sets the `Authorization` header for all HTTP requests:
```js
client.basicAuth('mark', 'mysupersecretpassword');
```
### Upgrades
If you successfully negotiate an Upgrade with the HTTP server, an
`upgradeResult` event will be emitted with the arguments `err`, `res`, `socket`
and `head`. You can use this functionality to establish a WebSockets
connection with a server. For example, using the
[watershed](https://github.com/jclulow/node-watershed) library:
```js
var ws = new Watershed();
var wskey = ws.generateKey();
var options = {
path: '/websockets/attach',
headers: {
connection: 'upgrade',
upgrade: 'websocket',
'sec-websocket-key': wskey,
}
};
client.get(options, function(err, res, socket, head) {
res.once('upgradeResult', function(err2, res2, socket2, head2) {
var shed = ws.connect(res2, socket2, head2, wskey);
shed.on('text', function(msg) {
console.log('message from server: ' + msg);
shed.end();
});
shed.send('greetings program');
});
});
```