npm package: @rigil/apifetch
Description: library wrapping fetch for easier usage
Author: Richard Gill
Latest version: 2.0.0

# apifetch

An API focused simplified **fetch**.

## Install

```
$ npm i @rigil/apifetch
```

## Basic usage

```js
import api from '@rigil/apifetch'

// with import we get a default instance of the API
// we can change a few properties (see options paragraph)
api.setBaseURL('http://myserver.api/v1')

// and make requests, JSON by default
let allUSers = await api.get('users')
let oneUser = await api.get('users', {
  email: 'username@domain.any'
})

// on POST requests the second argument is for the request body, the third
// the third will make aditional url parameters
let newUser = await api.post('users', {
  email: 'username@domain.any',
  password: 'secret'
})

await api.delete('users', { id: newUser.id })
```

## Additional instances

But we can create new instances with specific options.
```js
import { createApiInstance } from '@rigil/apifetch'

const codebergApi = createApiInstance({
    baseURL: 'https://codeberg.org/api/v1'
})

export codebergApi
```

## Options

The **apifetch** object uses a set of options for each instance.
You can set there any [fetch options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters),
plus a few ones that are specific to **apifetch**:

| Option | Default | Description |
|--------|---------|-------------|
| **baseURL** | `''` | URL prefix for all requests |
| **autoJson** | `true` | enables automatically converting data to JSON in requests and JSON to data in responses |
| **cacheTime** | `DEFAULT_TIMEOUT` | enables cache feature using a timeout in milliseconds (`0` will disable cache) |
| **beforeRequest** | `[]` | see [Interceptors](#Interceptors) |
| **afterResponse** | `[]` | see [Interceptors](#Interceptors) |

## Methods

The first set of methods are to alter options after instance creation.

### setBaseURL(baseURL)

It's the URL prefix used for all requests. Hence, the *resource* parameter in request calls can be relative to this prefix, and really represent a resource.
```js
api.setBaseURL('https://codeberg.org/api/v1')
```

### setAutoJson(enable)

If set, any request with a *body*, and if no `Content-Type` header has been specified, will have the body converted to a JSON string.

In the other way, all responses with a `Content-Type` matching `/json/` will have their bodies converted to objects (or arrays)

If not enabled, the request requires a *body* as described by the `fetch` API and the response will be the bare *body* as returned by `fetch`.

### setCacheTime(milliseconds)

For all `GET` requests, a cache is maintained to avoid fetching the same data too quickly over the network. This cache is enabled by default to 1 second, but can be adjusted this way, or disabled with a cache time of `0`.

When data is cached for a specific URL, no network fetch is performed at all (unless a request interceptor is made to do it).
```js
api.setCacheTime(0)
```

### setAuthorization(token, type)

The `Authorization` header can be automatically set for all requests to manage... authorization.
For this, you call this method with a *token* (or not to remove the authorization), and optionnaly a *type* which will be the header content prefix (defaults to `Bearer`).
```js
// enable authorization if mytoken is not null
api.setAuthorization(myToken) // Authorization: Bearer {myToken}
// set authorization to API client ID
api.setAuthorization(clientID, 'ClientID') // Authorization: ClientID {clientID}
// remove authorization header from all requests
api.setAuthorization()
api.setAuthorization(false) // same, more explicit
```

### Request methods

Unlike `fetch` you don't put query parameters in the URL directly (although you can if you want). The *resource* centric API style of **apifetch** separates the endpoint (base URL) from the *resource* denomination and its filtering through parameters.

The *params* parameter is an object containing query parameters (they all need to have a value, so `myurl?isNotPossible` is not available).

It's not possible either to mix query parameters in the *resource* **and** *params* parameters. **apifetch** is dumb and will try to insert two `?` in the URL.

```js
api.get(resource, params, additionalFetchOptions)
api.post(resource, body, params, additionalFetchOptions)
api.put(resource, body, params, additionalFetchOptions)
api.patch(resource, body, params, additionalFetchOptions)
api.delete(resource, params, additionalFetchOptions)
api.head(resource, params, additionalFetchOptions)
```

There is also a generic call where you can set your own method:
```js
api.request(method, resource, params, body, additionalFetchOptions)
```

## Interceptors

Interceptors permit to tweak requests and responses around `fetch` calls, through the **beforeRequest** and **afterResponse** options, which should be arrays of functions.

In **beforeRequest** each request function receives the final *url* and options passed to `fetch` and can either return:

* a `FetchParams` object containing modified *url* and *options* in its *input* and *init* properties respectively
* a `Response` object, in which case process is stopped and this response is returned to caller (hint: that's how mock can be implemented)
```js
import { FetchParams, createApiInstance } from 'apifetch'

const api = createApiInstance({
  // ...
  beforeRequest: [
    // dummy request interceptor
    async (url, options) => new FetchParams(url, options),
    // shortcuting interceptor sending a response
    async (url, options) => new Response('{}', {
      status: 200,
      statusText: 'I faked it!',
    })
  ]
})
```

Likewise, after `fetch` returns its response, this one can be altered with **afterResponse** interceptors with the request's *url* and *options*, plus a copy of the latest *response* (from `fetch` or previous interceptor).
```js
const api = createApiInstance({
  // ...
  afterResponse: [
    // dummy response interceptor
    async (url, options, response) => response
  ]
})
```

>**IMPORTANT**
>
> Interceptors must be inserted on the instance creation, or sent through requests options parameters. They can't be altered with methods.
>
> But you can manage arrays yourself and modify them after giving them by reference, to get the same result.

```js
const myRequestInterceptors = [
  async (url, options) => new FetchParams(url, options)
]

const api = createApiInstance({
  // ...
  beforeRequest: myRequestInterceptors
})

myRequestInterceptors.push(
  async (url, options) => new Response('{}', {
    status: 200,
    statusText: 'I faked it!',
  })
)
```