REST API Client Library

# Purest

[![npm-version]][npm] [![test-ci-img]][test-ci-url] [![test-cov-img]][test-cov-url] [![snyk-vulnerabilities]][snyk]

> _REST API Client Library_

```js
var purest = require('purest')
var google = purest({provider: 'google'})

await google
  .query('youtube')
  .select('channels')
  .where({forUsername: 'GitHub'})
  .auth(token)
  .request()
```

## Table of Contents

> _This is Purest **v4**, for older releases take a look at [v3] and [v2]_

- **[Introduction](#introduction)**
- **[Purest Options](#purest-options)**
- **[Request Options](#request-options)**
- **[Examples](#examples)**
- **[Article]**

---

## Introduction

> _**Purest** is a tool for building **expressive** REST API clients_

### Default Endpoint

Here is a basic configuration for Google:

```json
{
  "google": {
    "default": {
      "origin": "https://www.googleapis.com",
      "path": "{path}",
      "headers": {
        "authorization": "Bearer {auth}"
      }
    }
  }
}
```

The above configuration can be used to instantiate that provider:

```js
var google = purest({provider: 'google', config})
```

Then we can request some data from YouTube:

```js
var {res, body} = await google
  .get('youtube/v3/channels')
  .qs({forUsername: 'GitHub'})
  .auth(token)
  .request()
```

### Explicit Endpoint

We can define explicit endpoint for accessing the YouTube API:

```json
{
  "google": {
    "default": {
      "origin": "https://www.googleapis.com",
      "path": "{path}",
      "headers": {
        "authorization": "Bearer {auth}"
      }
    },
    "youtube": {
      "origin": "https://www.googleapis.com",
      "path": "youtube/{version}/{path}",
      "version": "v3",
      "headers": {
        "authorization": "Bearer {auth}"
      }
    }
  }
}
```

And then request the same data:

```js
var {res, body} = await google('youtube')
  .get('channels')
  .qs({forUsername: 'GitHub'})
  .auth(token)
  .request()
```

### Defaults

Every method in Purest can also be preconfigured with a value:

```js
var google = purest({provider: 'google', config,
  defaults: {auth: token}
})
```

Then we no longer need to set the access token on each request:

```js
var {res, body} = await google('youtube')
  .get('channels')
  .qs({forUsername: 'GitHub'})
  .request()
```

### Method Aliases

Each method in Purest can have multiple aliases defined for it:

```js
var google = purest({provider: 'google', config,
  defaults: {auth: token},
  methods: {get: ['select'], qs: ['where']}
})
```

And then use it like this:

```js
var {res, body} = await google('youtube')
  .select('channels')
  .where({forUsername: 'GitHub'})
  .request()
```

---

## Purest Options

> _**Purest** is a flexible tool for **abstracting** out REST APIs_

```js
var google = purest({config: {}, provider: 'google', defaults: {}, methods: {}})
```

| Key            | Type | Description
| :-             | :-:  | :-
| **`provider`** | `''` | Provider name to initialize from the list of providers found in `config`
| **`config`**   | `{}` | Providers configuration to use
| **`defaults`** | `{}` | Any supported configuration option set by default, see below
| **`methods`**  | `{}` | List of methods and their aliases to use with this instance

---

## Request Options

> _**Purest** is built on top of a **[powerful HTTP Client][request-compose]**_

### URL Options

| Option      | Description
| :-          | :-
| `origin`    | The protocol and domain part of the URL, can contain `{subdomain}` token
| `path`      | The path part of the URL, can contain `{version}`, `{path}` and `{type}` tokens
| `subdomain` | Subdomain part of the URL to replace in `origin`
| `version`   | Version string to replace in `path`
| `type`      | Type string to replace in `path`, typically `json` or `xml`

### HTTP Methods

All HTTP methods `get` `head` `post` `put` `patch` `options` `delete` `trace` `connect` accept a string to replace the `{path}` configuration token with, or absolute URL to set the entire `url`.

### Request Options

| Option     | Type                  | Description
| :--        | :--                   | :--
| `method`   | `'string'` | Request method, implicitly set if one of the above HTTP Methods is used
| `url`      | `'string'` [`url object`][url-parse] | Absolute URL, automatically constructed if the URL Options above are being used, or absolute URL is passed to any of the HTTP Methods above
| `proxy`    | `'string'` [`url object`][url-parse] | Proxy URL; for HTTPS you have to use [tunneling][tunnel-agent] [agent][proxy-agent] instead
| `qs`       | `{object}` `'string'` | URL querystring
| `headers`  | `{object}` | Request headers
| `form`     | `{object}` `'string'` | `application/x-www-form-urlencoded` request body
| `json`     | `{object}` `'string'` | JSON encoded request body
| `multipart`| `{object}` `[array]`  | `multipart/form-data` as object or `multipart/related` as array request body using [request-multipart]
| `body`     | `'string'` [`Buffer`][buffer] [`Stream`][stream-readable] | Raw request body
| `auth`     | `'string'` `['string', 'string']` `{user, pass}`        | String or array of strings to replace the `{auth}` configuration token with, or Basic authorization as object
| `oauth`    | `{object}` | OAuth 1.0a authorization using [request-oauth]
| `encoding` | [`'string'`][buffer-encoding] | Response body encoding
| `redirect` | `{object}` | HTTP redirect [configuration][redirect-config]
| `timeout`  | `number` | Request timeout in milliseconds
| `agent`    | [`Agent`][agent] | HTTP agent

### Response Options

#### `request`

- buffers the response body
- decompresses `gzip` and `deflate` encoded bodies with valid `content-encoding` header
- converts the response body to string using `utf8` encoding by default
- tries to parse `JSON` and `querystring` encoded bodies with valid `content-type` header

Returns either String or Object.

#### `buffer`

- buffers the response body
- decompresses `gzip` and `deflate` encoded bodies with valid `content-encoding` header

Returns [Buffer][buffer].

#### `stream`

Returns the response [Stream][stream-incoming-message].

### Node Core Options

Any other HTTP request option not explicitly exposed in Purest can be set using any of the response methods:

```js
await google.request({socketPath: ''})
await google.buffer({socketPath: ''})
await google.stream({socketPath: ''})
```

### Endpoint

The explicit `endpoint` configuration can be accessed in various ways:

```js
// as argument to the Purest instance
await google('youtube')
// using the option name
await google.endpoint('youtube')
// or the default method alias defined for it
await google.query('youtube')
```

---

## Examples

> _**Purest** comes with a **[fancy logger][request-logs]**_

```bash
npm i --save-dev request-logs
```

```bash
DEBUG=req,res,body,json node examples/file-name.js 'example name'
```

| Category | Topic | Providers | Example
| :-       | :-    | :-        | :-
| **OAuth 2.0** | _Refresh Access Tokens_ | `box` `google` `twitch` | [Refresh access tokens][refresh-token]
| **OpenID Connect** | *Verify id_token* | `auth0` `google` `microsoft` | [Discover public keys and verify id_token signature][openid-connect]
| **OAuth 1.0a** | _OAuth 1.0a_ | `flickr` `trello` `twitter` | [Get user profile][oauth-1]
| **Storage** | _Multipart, Streams_ | `box` `dropbox` `drive` | [Upload files][file-stream]
| **Storage** | _HTTP Streams_ | `box` `dropbox` | [Stream file from DropBox to Box][http-stream]

> _Get access tokens using **[Grant]**_


  [npm-version]: https://img.shields.io/npm/v/purest.svg?style=flat-square (NPM Version)
  [test-ci-img]: https://img.shields.io/travis/simov/purest/master.svg?style=flat-square (Build Status)
  [test-cov-img]: https://img.shields.io/coveralls/simov/purest.svg?style=flat-square (Test Coverage)
  [snyk-vulnerabilities]: https://img.shields.io/snyk/vulnerabilities/npm/purest.svg?style=flat-square (Vulnerabilities)

  [npm]: https://www.npmjs.com/package/purest
  [test-ci-url]: https://github.com/simov/purest/actions/workflows/test.yml
  [test-cov-url]: https://coveralls.io/r/simov/purest?branch=master
  [snyk]: https://snyk.io/test/npm/purest

  [v3]: https://github.com/simov/purest/tree/3.x
  [v2]: https://github.com/simov/purest/tree/2.x
  [article]: https://dev.to/simov/purest-53k0

  [request-compose]: https://github.com/simov/request-compose
  [request-oauth]: https://github.com/simov/request-oauth
  [request-multipart]: https://github.com/simov/request-multipart
  [request-cookie]: https://github.com/simov/request-cookie
  [request-logs]: https://github.com/simov/request-logs

  [grant]: https://github.com/simov/grant
  [redirect-config]: https://github.com/simov/request-compose#redirect
  [tunnel-agent]: https://github.com/simov/request-compose/blob/master/examples/misc-tunnel-agent.js
  [proxy-agent]: https://github.com/simov/request-compose/blob/master/examples/misc-proxy-agent.js
  [methods.json]: https://github.com/simov/purest/blob/master/config/methods.json

  [refresh-token]: https://github.com/simov/purest/blob/master/examples/refresh-token.js
  [openid-connect]: https://github.com/simov/purest/blob/master/examples/openid-connect.js
  [oauth-1]: https://github.com/simov/purest/blob/master/examples/oauth-1.js
  [file-stream]: https://github.com/simov/purest/blob/master/examples/file-stream.js
  [http-stream]: https://github.com/simov/purest/blob/master/examples/http-stream.js

  [url-parse]: https://nodejs.org/dist/latest-v10.x/docs/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
  [buffer]: https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html
  [buffer-encoding]: https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_buffers_and_character_encodings
  [stream-readable]: https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_class_stream_readable
  [stream-incoming-message]: https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_class_http_incomingmessage
  [agent]: https://nodejs.org/docs/latest-v10.x/api/http.html#http_class_http_agent
