# 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}"
}
}
}
}
```
Our configuration can be used to instantiate that provider:
```js
var google = purest({provider: 'google', config})
```
Finally 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 static value:
```js
var google = purest({provider: 'google', config,
defaults: {auth: token}
})
```
Then we no longer need to set the access token on every request:
```js
var {res, body} = await google('youtube')
.get('channels')
.qs({forUsername: 'GitHub'})
.request()
```
### Method Aliases
But what if we want to make our API more expressive? What if we want to make it our own:
```js
var google = purest({provider: 'google', config,
defaults: {auth: token},
methods: {get: ['select'], qs: ['where']}
})
```
Yes we can:
```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 replace 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 | Topics | Providers | Examples
| :- | :- | :- | :-
| **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