Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/D-Marc1/far-fetch
Modern Fetch API wrapper for simplicity.
https://github.com/D-Marc1/far-fetch
Last synced: 3 months ago
JSON representation
Modern Fetch API wrapper for simplicity.
- Host: GitHub
- URL: https://github.com/D-Marc1/far-fetch
- Owner: D-Marc1
- License: mit
- Created: 2020-05-12T02:50:06.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2022-11-28T03:04:55.000Z (almost 2 years ago)
- Last Synced: 2024-07-03T16:19:25.474Z (4 months ago)
- Language: JavaScript
- Homepage: https://farfetch.js.org
- Size: 94.7 KB
- Stars: 58
- Watchers: 6
- Forks: 6
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# FarFetch Class
Modern Fetch API wrapper for simplicity.
## Install
```
npm i @websitebeaver/far-fetch
```Then include it in the files you want to use it in like so:
```js
import FarFetch from '@websitebeaver/far-fetch';// If you need to manually handle errors
import FarFetch, { FarFetchError } from '@websitebeaver/far-fetch';
```## Instantiating Class
```js
const ff = new FarFetch();
```This is how you'd create a class in its simplest form in `FarFetch`. You can
check out [all the options you can use here](#new-farfetchoptions).It is recommended to then import the initialized class on every page used.
## Why Use FarFetch?
While JavaScript's native `Fetch API` was an amazing feature added, it
introduced a myriad of inconsistencies, which cause you to have to copy and
paste StackOverflow questions for simple boilerplate code often times. This is
especially the case with uploading files and I find it hard to believe anyone
just straight memorizes how to do it. There's so many things to think about. It
would have been so much better in a unified experience.The core philosophy of `FarFetch` to is keep things as similar as possible to
native JavaScript fetch, and merely improve upon it. This way, you're not really
learning a completely new API. Instead, the aim of this class is almost to pitch
which features I think are missing in `Fetch API`. The aim of this class isn't
to recreate the wheel, but rather, to improve upon `Fetch API`, with a super
thin wrapper. The main advantages over vanilla `Fetch` are as follows:1. Ability to call syntactic sugar methods like `ff.get()`, `ff.post()`,
`ff.put`, etc., rather than `fetch(url, { method: 'GET' })`. `FarFetch`
allows both ways.
2. Ability to ["automatically" throw and handle errors](#error-handling) for
every call in a unified manner with a global error handler.
3. Ability to use default [request init options](#passing-in-fetch-api-init-options)
on every call.
4. Ability to do actions before every request
with [`beforeSend()` and `afterSend(response)` hooks](#beforeafter-send-hook).
5. Ability to [pass data to each call in a consistent manner](#passing-in-data-to-request).
6. Ability to [upload files](#uploading-files) in a consistent manner.- [FarFetch Class](#farfetch-class)
- [Install](#install)
- [Instantiating Class](#instantiating-class)
- [Why Use FarFetch?](#why-use-farfetch)
- [Passing in Data to Request](#passing-in-data-to-request)
- [GET Request](#get-request)
- [POST Request](#post-request)
- [application/x-www-form-urlencoded Request](#applicationx-www-form-urlencoded-request)
- [Array or Object as Value for Key for GET Request](#array-or-object-as-value-for-key-for-get-request)
- [Passing in URLParams to Request](#passing-in-urlparams-to-request)
- [Uploading Files](#uploading-files)
- [Uploading One File](#uploading-one-file)
- [Uploading Multiple Files](#uploading-multiple-files)
- [Uploading Multiple Files with Distinct File Names](#uploading-multiple-files-with-distinct-file-names)
- [Passing in Fetch API init options](#passing-in-fetch-api-init-options)
- [Set Options for Single Request](#set-options-for-single-request)
- [Set Global Options for Every Request](#set-global-options-for-every-request)
- [Set Dynamic Options for Every Request](#set-dynamic-options-for-every-request)
- [Getting Response](#getting-response)
- [Retrieving Response Data](#retrieving-response-data)
- [Set Base URL](#set-base-url)
- [Before/After Send Hook](#beforeafter-send-hook)
- [Turn off Before/After Send Hook on Single Request](#turn-off-beforeafter-send-hook-on-single-request)
- [Error Handling](#error-handling)
- [Modifying the Default Error Message Template](#modifying-the-default-error-message-template)
- [Overriding Default Error Message for Single Request](#overriding-default-error-message-for-single-request)
- [Catching Exceptions Manually](#catching-exceptions-manually)
- [Empty Try/Catch](#empty-trycatch)
- [API](#api)
- [FarFetch](#farfetch)
- [new FarFetch([options])](#new-farfetchoptions)
- [farFetch.fetch(url, options) ⇒Promise.<ResponsePlus>
](#farfetchfetchurl-options--promiseresponseplus)
- [farFetch.get(url, [...options]) ⇒Promise.<ResponsePlus>
](#farfetchgeturl-options--promiseresponseplus)
- [farFetch.post(url, [...options]) ⇒Promise.<ResponsePlus>
](#farfetchposturl-options--promiseresponseplus)
- [farFetch.put(url, [...options]) ⇒Promise.<ResponsePlus>
](#farfetchputurl-options--promiseresponseplus)
- [farFetch.patch(url, [...options]) ⇒Promise.<ResponsePlus>
](#farfetchpatchurl-options--promiseresponseplus)
- [farFetch.delete(url, [...options]) ⇒Promise.<ResponsePlus>
](#farfetchdeleteurl-options--promiseresponseplus)
- [farFetch.head(url, [...options]) ⇒Promise.<ResponsePlus>
](#farfetchheadurl-options--promiseresponseplus)
- [FarFetchError ⇐Error
](#farfetcherror--error)
- [new FarFetchError(message)](#new-farfetcherrormessage)
- [RequestException :Object
](#requestexception--object)
- [ResponsePlus :Object
](#responseplus--object)
- [RequestOptionsNoInit :Object
](#requestoptionsnoinit--object)
- [RequestOptions :Object
](#requestoptions--object)
- [dynamicOptionsCallback ⇒RequestInit
](#dynamicoptionscallback--requestinit)
- [beforeSendCallback :function
](#beforesendcallback--function)## Passing in Data to Request
Passing in data in `Fetch API` is exceedingly inconsistent. In this regard, it
really took a step backwards from how jQuery implemented passing in data, in my
opinion, at least from a usability standpoint. Of course `Fetch API`'s `body`
options offers more versatility, which is why `FarFetch` supports using `body`.
However, it really shouldn't be necessary in the majority of use cases. Adding
data to a `GET` and `POST` request is done in two separate ways in `Fetch API`.
`GET` requests must use appended URL query parameters, while `POST` requests
generally use a stringified object used as the `body` property.### GET Request
**Fetch API**
```js
async getPerson() {
const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };const queryString = `?${new URLSearchParams(data)}`;
const response = await fetch(`https://example.com/people${queryString}`, {
method: 'GET',
});if(!response.ok) throw new Error('Server error.');
return response.json();
}
```**FarFetch**
```js
async getPerson() {
const { responseJSON } = await ff.get('https://example.com/people', {
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
});return responseJSON;
}
```### POST Request
**Fetch API**
```js
async addPerson() {
const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };const response = await fetch(`https://example.com/people`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});if(!response.ok) throw new Error('Server error.');
return response.json();
}
```**FarFetch**
```js
async addPerson() {
const { responseJSON } = await ff.post('https://example.com/people', {
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
});return responseJSON;
}
```### application/x-www-form-urlencoded Request
**Fetch API**
```js
async addPerson() {
const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };const response = await fetch(`https://example.com/people`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(data),
});if(!response.ok) throw new Error('Server error.');
return response.json();
}
```**FarFetch**
```js
async addPerson() {
const { responseJSON } = await ff.post('https://example.com/people', {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
});return responseJSON;
}
```### Array or Object as Value for Key for GET Request
**Fetch API**
```js
async getPerson() {
const queryString = `?${new URLSearchParams({
name: 'Bobby Big Boy',
hobbies: JSON.stringify(['collecting stamps', 'sports']),
location: JSON.stringify({ city: 'Miami', state: 'Florida' }),
})}`;const response = await fetch(`https://example.com/people${queryString}`, {
method: 'GET',
});if(!response.ok) throw new Error('Server error.');
return response.json();
}
```This can be a bit cumbersome to have to have to `JSON.stringify()` each value
that's an array or object. `FarFetch` automatically takes care of this.**FarFetch**
```js
async getPerson() {
const { responseJSON } = await ff.get(`https://example.com/people`, {
data: {
name: 'Bobby Big Boy',
hobbies: ['collecting stamps', 'sports'],
location: { city: 'Miami', state: 'Florida' },
},
});return responseJSON;
}
```Notice how each request is completely predictable in `FarFetch` and doesn't
require you to throw an exception if it's not a status code in the `200-299`
range (`response.ok`). Sure, using the native javascript `Fetch API` isn't
horrible anymore in regular Javascript, thanks to features like
`URLSearchParams`, but it's so much easier to not have to
think much when you program. `FarFetch`'s consistent API makes it a breeze to
make any sort of request.*Note: The content type header for a `POST`, `PUT` and `PATCH` request will
always be `application/json`, unless `application/x-www-form-urlencoded` is
specified or if it's a file upload*.### Passing in URLParams to Request
This is specifically for converting to a `URL query string`, which differs from
`data`, which *detects* the default type (`body` or `query string`).It's recommended to strictly use the `URLParams` option in cases where `POST`,
`POST` and `PATCH` are used, as the default behavior for passing in `data` in
this case will result in passing in the object to `body`. You can still pass in
`URLParams` on `GET`, `HEAD` and `DELETE`, but you can't combine it with `data`.
This is for consistency purposes, as it would be confusing as to why you'd be
using `data` and `URLParams` in the latter ones, as they would achieve the same
result in this case. In fact, `FarFetch` even throws an exception in this
senario.```js
async addPerson() {
const { responseJSON } = await ff.post('https://example.com/people', {
URLParams: { weight: 75 },
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
});return responseJSON;
}
```Converted to `Fetch API`, the following will result in:
```js
const response = await fetch(`https://example.com/people?weight=75`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Bobby Big Boy', gender: 'Male', age: 5 }),
});
```## Uploading Files
### Uploading One File
**Fetch API**
```js
async uploadFile() {
const [file] = document.querySelector('#my-file').files;const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };
const formData = new FormData();
formData.append('file', file);
Object.keys(data).forEach((key) => {
formData.append(key, data[key]); // Add server data to formData
});const response = await fetch('https://example.com/people', {
method: 'POST',
body: formData,
});if(!response.ok) throw new Error('Server error.');
}
```**FarFetch**
```js
async uploadFile() {
const [file] = document.querySelector('#my-file').files;await ff.post('https://example.com/people', {
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
files: file,
});
}
```### Uploading Multiple Files
**Fetch API**
```js
async uploadFiles() {
const files = document.querySelector('#my-files').files;const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };
const formData = new FormData();
Object.keys(data).forEach((key) => {
formData.append(key, data[key]); // Add server data to formData
});files.forEach((file) => {
formData.append('files[]', file); // Add files array to formData
});await fetch('https://example.com/people', {
method: 'POST',
body: formData,
});if(!response.ok) throw new Error('Server error.');
}
```**FarFetch**
```js
async uploadFiles() {
const files = document.querySelector('#my-files').files;await ff.post('https://example.com/people', {
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
files,
});
}
```### Uploading Multiple Files with Distinct File Names
**Fetch API**
```js
async uploadFiles() {
const photos = document.querySelector('#photos').files;
const videos = document.querySelector('#videos').files;
const documents = document.querySelector('#documents').files;const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };
const formData = new FormData();
Object.keys(data).forEach((key) => {
formData.append(key, data[key]); // Add server data to formData
});photos.forEach((photo) => {
formData.append('photos[]', photo); // Add files array to formData
});videos.forEach((video) => {
formData.append('videos[]', video); // Add files array to formData
});documents.forEach((document) => {
formData.append('documents[]', document); // Add files array to formData
});await fetch('https://example.com/people', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: formData,
});if(!response.ok) throw new Error('Server error.');
}
```**FarFetch**
```js
async uploadFiles() {
const photos = document.querySelector('#photos').files;
const videos = document.querySelector('#videos').files;
const documents = document.querySelector('#documents').files;await ff.post('https://example.com/people', {
data: { name: 'Bobby Big Boy', gender: 'Male', age: 22 },
files: { photos, videos, documents },
});
}
```Look at how much more comprehensible the code becomes with `FarFetch`. This is
practically even readable by a non-programmer, as this reads as: *Let's add a 22
year old man named Bobby and upload his following files: photos, videos and
documents*.## Passing in Fetch API init options
`FarFetch` accepts all [Fetch API init
options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters).
It's even possible to pass in the native fetch `body`, though its use is
discouraged in most cases, in favor of, `FarFetch`'s `data` parameter.### Set Options for Single Request
```js
await ff.get('https://example.com', {
headers: { 'Content-Type': 'application/json' },
cache: 'reload',
})
```### Set Global Options for Every Request
This is really handy for when you know for a fact you will be using the same
options on either every request or nearly all. You can accomplish this by
declaring these options when you instantiate `FarFetch`.```js
const ff = new FarFetch({
headers: { 'Content-Type': 'application/json' },
cache: 'reload',
});
```### Set Dynamic Options for Every Request
Sometimes you might not want to set a particular option when FarFetch is
created. Let's say you're using a login system. You don't want to have the `JWT`
be evaluated when you instantiate the class, since it won't work properly if a
user accesses the page logged out. The header would never reevaluate. This is
why `FarFetch` has allows you to return options you want to use on specific
conditions on the global `dynamicOptions()` function. These options will then
get deep merged, with the `dynamicOptions()` return taking precedence.
Obviously passing options into a specific request will take the highest
precedence of them all, however.```js
const ff = new FarFetch({
headers: { 'Content-Type': 'application/json' },
cache: 'reload',
dynamicOptions() {
// Use authorization header if token set in localStorage
if (localStorage.getItem('token')) {
return {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
};
}
},
});
```So if you're logged in, your request would have the following options.
```js
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
cache: reload,
}
```Notice how the `Content-Type` is set to `text/plain`, rather than
`application/json`. As stated before, this is because the return on
`beforeSend()` takes precedence on the deep merge.## Getting Response
### Retrieving Response Data
`FarFetch` returns a [Response
object](https://developer.mozilla.org/en-US/docs/Web/API/Response), so you can
use [the `Body`
methods](https:/developer.mozilla.org/en-US/docs/Web/API/Body#Methods):
`arrayBuffer()`, `blob()`, `formData()`, `json()` and `text()`. This is exactly
how you'd be doing it in native `Fetch` as well.`FarFetch` supports the vanilla `Fetch` way of retrieving data, by awaiting for
the the `Response` `Body` and transforming it to your type.```js
const response = await ff.get('https://example.com/people');const responseJSON = await response.json();
return responseJSON;
```You can also use `FarFetch`'s handy `responseJSON` and `responseText`
properties for your convenience, instead of having to await for either `response.json()` or
`response.text()`, if the response header type is either `application/json` or
`text/plain`, respectively. These are properties that were simply added to the
`Response` object. What's also nice about this is that it ensures that getting
the JSON won't result in an error, due to a mismatch in header, as `FarFetch`
checks for this already, internally.```js
const { responseJSON } = await ff.get('https://example.com/people');return responseJSON;
``````js
const { responseText } = await ff.get('https://example.com/people');return responseText;
```## Set Base URL
Most applications will likely use the same domain for most or even all requests.
`FarFetch` has a `baseURL` option you can use when you instantiate the class.```js
const ff = new FarFetch({
baseURL: 'https://example.com',
});
```Now request like the following will work.
```js
await ff.get('/people');
```But what if you want to use a different base URL for just a few requests in your
application? `FarFetch` automatically detects if an absolute path is used, and
will override the `baseURL`.```js
await ff.get('https://notexample.com/posts');
```## Before/After Send Hook
You can use the built-in `beforeSend()` hook to do something before every
request and the `afterSend(response)` one to do something after every request.```js
const ff = new FarFetch({
beforeSend({
url,
fetchAPIOptions,
data,
URLParams,
files,
errorMsg,
errorMsgNoun,
globalBeforeSend,
globalAfterSend,
defaultOptionsUsed,
}) {
console.log('do this before every request');
},
afterSend(response) {
console.log('do this after every request');
},
});
```### Turn off Before/After Send Hook on Single Request
You might want to use the `beforeSend()` or `afterSend(response)` hook on nearly
all requests, but turn it off certain ones.```js
await ff.get('http://example.com/', {
globalBeforeSend: false,
globalAfterSend: false,
});
```## Error Handling
Another annoyance of `Fetch API` is that it doesn't automatically throw an error
on a failed request, and forces you to throw your own.**Fetch API**
```js
const data = { name: 'Bobby Big Boy', gender: 'Male', age: 5 };try {
const response = await fetch('https://example.com/people', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});if(!response.ok) throw new Error('Server error.');
} catch {
alert('Error adding person');
}
```Thankfully you don't need to worry about this with `FarFetch`. With `FarFetch`,
you can just append the noun to `errorMsgNoun` and it'll append to a
default template, dependent on the CRUD type.**FarFetch**
```js
try {
await ff.post('https://example.com/person', {
data: { name: 'Bobby Big Boy', gender: 'Male', age: 5 },
errorMsgNoun: 'person'
});
} catch {}
```Your global handler would then handle it as such:
```js
const ff = new FarFetch({
errorHandler({ error, userMessage, response }) {
if(response.status === 401) { // Unauthorized
router.push('/login'); // Go to login page if logged out
}// Error message will be presented to the user in an alert
alert(userMessage);
},
});
```If an error occurs, this will result in an alert with the following message:
> Error adding person.
You might be wondering how this works behind the scenes.
Here's the basic template of what going into the `userMessage` parameter
property.```js
let action = '';if (method === 'GET' || method === 'HEAD') {
action = 'fetching';
} else if (method === 'POST') {
action = 'adding';
} else if (method === 'PUT' || method === 'PATCH') {
action = 'updating';
} else if (method === 'DELETE') {
action = 'deleting';
}const userMessage = `Error ${action} ${errorMsgNoun}`;
```### Modifying the Default Error Message Template
You can even override this default template with the `errorMsgTemplate`
property, which accepts function.```js
const ff = new FarFetch({
errorMsgTemplate: ({ method, errorMsgNoun }) => {
let action = '';if (method === 'GET' || method === 'HEAD') {
action = 'retrieving';
} else if (method === 'POST') {
action = 'posting';
} else if (method === 'PUT' || method === 'PATCH') {
action = 'changing';
} else if (method === 'DELETE') {
action = 'removing';
}return `Error ${action} ${errorMsgNoun}.`;
},
});
```In case you couldn't tell by this contrived example, I merely used synonyms of
the default template. However, this allows flexibility to tailor to
custom requirements.### Overriding Default Error Message for Single Request
Sometimes you might just want to change the message for a unique request. You
can accomplish this via the `errorMsg` property.```js
await ff.get('https://example.com/users', {
errorMsg: 'Oh no! We are having trouble retrieving your friends!',
});
```### Catching Exceptions Manually
Using the global `errorHandler()`, along with `errorMsgNoun` or `errorMsg`
should work fine in most cases, but sometimes you might need to handle multiple
cases. You can easily achieve this by simply omitting both `errorMsgNoun` and
`errorMsg`. `FarFetch` will then know not to run the global error handler. You
can then can the errors in a `try/catch`. Take a register account example for
instance.```js
async register(type) {
try {
const response = await ff.post(`http://127.0.0.1:3333/${type}`, {
data: {
email: this.email,
password: this.password,
},
});const responseData = await response.json();
localStorage.setItem('token', responseData.token);
this.$router.push('/');
} catch (e) {
if (e instanceof FarFetchError) {
let userMessage = '';const { response, error } = e;
if (response.status === 409) { // Conflict
userMessage = 'Email is already in system';
} else if (response.status === 400) { // Validation failed
const { field, validationMsg } = response.responseJSON;userMessage = `${field} is ${validationMsg}`;
}
ff.errorHandler({ error, response, userMessage });
} else {
userMessage = e.message;ff.errorHandler({ error, userMessage });
}
}
}
```Each case his handled individually. You can then add the string to the global
error handler you created on instantiation, with
`ff.errorHandler({ error, response, userMessage })`.### Empty Try/Catch
It is **required** to use a `try/catch` on every request in `FarFetch`, in order
to stop execution on an error. This isn't because there's anything
unique about how this library does anything; the same recommendation would apply
to any async request in JavaScript that relies on sequential steps occurring on
success. `FarFetch` specifically throws an error to stop the further execution
of code. Perhaps an example would help illustrate my point better.Consider how a simple register account might work, like in the previous example.
You make a request and if there aren't any issues, like email already taken or
validation issues, you set the `localStorage` to the `JWT` and then route to the
logged in page. The problem is that if you an exception isn't thrown, there's
nothing to stop the script's execution, and it'll *always* set `localStorage`
and try to route to the logged in route, even if the request failed.# API
## Classes
- FarFetch
CRUD class to simplify fetch API and uploading.
- FarFetchError
FarFetch Error class.
## Typedefs
-
RequestException :Object
-
The Request exception object.
-
ResponsePlus :Object
-
Request object plus responseJSON and responseText properties if correct header type.
-
RequestOptionsNoInit :Object
-
The request object options without Fetch API options.
-
RequestOptions :Object
-
The request object options.
-
dynamicOptionsCallback ⇒RequestInit
-
Callback for global dynamic options. Allows a dynamic option to be set, like a token stored in
localStorage. -
beforeSendCallback :function
-
Callback for global before send hook.
-
afterSendCallback :function
-
Callback for global after send hook.
-
errorHandlerCallback :function
-
Callback for global error handler.
-
errorMsgTemplateCallback ⇒string
-
Callback for overriding default error message template.
## FarFetch
CRUD class to simplify fetch API and uploading.
**Kind**: global class
* [FarFetch](#FarFetch)
* [new FarFetch([options])](#new_FarFetch_new)
* [.fetch(url, options)](#FarFetch+fetch) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
* [.get(url, [...options])](#FarFetch+get) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
* [.post(url, [...options])](#FarFetch+post) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
* [.put(url, [...options])](#FarFetch+put) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
* [.patch(url, [...options])](#FarFetch+patch) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
* [.delete(url, [...options])](#FarFetch+delete) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
* [.head(url, [...options])](#FarFetch+head) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
### new FarFetch([options])
Create FarFetch object.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | Object
| {}
| Set options. |
| [options.baseURL] | string
| ''
| Base URL for each request. |
| [options.dynamicOptions] | [dynamicOptionsCallback
](#dynamicOptionsCallback) | | Function that allows a dynamic option to be set, like a token stored in localStorage. |
| [options.beforeSend] | [beforeSendCallback
](#beforeSendCallback) | | Function to do something before each fetch request. Can return object with RequestOptions to add or override options. |
| [options.afterSend] | [afterSendCallback
](#afterSendCallback) | | Function to do something after each fetch request. |
| [options.errorHandler] | [errorHandlerCallback
](#errorHandlerCallback) | | Global error handler. |
| [options.errorMsgTemplate] | [errorMsgTemplateCallback
](#errorMsgTemplateCallback) | | Function to modify the default error message template for `errorMsgNoun`. |
| [...options.defaultOptions] | RequestInit
| {}
| [Init options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) from Fetch API. |
**Example**
```js
const ff = new FarFetch({
baseURL: 'https://my-url.com',
dynamicOptions() {
// Use authorization header if token set in localStorage
if (localStorage.getItem('token')) {
return {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
}
}
},
beforeSend() {
console.log('Doing something before every request');
},
afterSend(response) {
console.log('Doing after before every request');
},
errorHandler({ error, userMessage, response }) {
if(response.status === 401) { // Unauthorized
router.push('/login');
}
alert(userMessage); // Error message from either errorMsg or errorMsgNoun will be used
},
headers: { 'Content-Type': 'application/json' },
});
```
### farFetch.fetch(url, options) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
Request function called on every CRUD function.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| options | Object
| |
| options.method | 'GET'
\| 'POST'
\| 'PUT'
\| 'PATCH'
\| 'DELETE'
\| 'HEAD'
| The CRUD method. |
| [...options.RequestOptions] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.fetch('https://my-website.com/users', {
method: 'GET',
data: { id: 23 },
errorMsgNoun: 'users',
});
```
### farFetch.get(url, [...options]) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
GET fetch request.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| [...options] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.get('https://my-website.com/users', {
data: { id: 23 },
errorMsgNoun: 'users',
});
```
### farFetch.post(url, [...options]) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
POST fetch request. Will default to `'Content-Type': 'application/json'` for the request header
if `FarFetch` data option is provided.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| [...options] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.post('https://my-website.com/user/23', {
data: { gender: 'male', age: 39 },
errorMsgNoun: 'user',
});
```
### farFetch.put(url, [...options]) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
PUT fetch request. Will default to `'Content-Type': 'application/json'` for the request header
if `FarFetch` data option is provided.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| [...options] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.put('https://my-website.com/user/47', {
data: { gender: 'female', age: 22 },
errorMsgNoun: 'user',
});
```
### farFetch.patch(url, [...options]) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
PATCH fetch request. Will default to `'Content-Type': 'application/json'` for the request
header if `FarFetch` data option is provided.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| [...options] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.patch('https://my-website.com/user/91', {
data: { age: 18 },
errorMsgNoun: 'user',
});
```
### farFetch.delete(url, [...options]) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
DELETE fetch request.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| [...options] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.delete('https://my-website.com/user/107', {
errorMsgNoun: 'user',
});
```
### farFetch.head(url, [...options]) ⇒ [Promise.<ResponsePlus>
](#ResponsePlus)
HEAD fetch request.
**Kind**: instance method of [FarFetch
](#FarFetch)
**Throws**:
- [RequestException
](#RequestException)
| Param | Type | Description |
| --- | --- | --- |
| url | string
| The URL. |
| [...options] | [RequestOptions
](#RequestOptions) | |
**Example**
```js
await ff.head('https://my-website.com/user/107');
```
## FarFetchError ⇐ Error
FarFetch Error class.
**Kind**: global class
**Extends**: Error
### new FarFetchError(message)
| Param | Type | Description |
| --- | --- | --- |
| message | string
\| object.<string, \*>
| Will be in the `message` property if a string or its own properties if object. |
## RequestException : Object
The Request exception object.
**Kind**: global typedef
**Properties**
| Name | Type | Description |
| --- | --- | --- |
| error | FarFetchError
| FarFetch error. |
| response | [ResponsePlus
](#ResponsePlus) | Fetch API response plus added properties for syntactic sugar. |
## ResponsePlus : Object
Request object plus responseJSON and responseText properties if correct header type.
**Kind**: global typedef
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| response | Response
| | Fetch API response. [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response). |
| [response.responseJSON] | Object
| | FarFetch added property that transforms the body to JSON for syntactic sugar if the same response header type. |
| [response.responseText] | string
| null
| FarFetch added property that transforms the body to text for syntactic sugar if the same response header type. |
## RequestOptionsNoInit : Object
The request object options without Fetch API options.
**Kind**: global typedef
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [data] | Object.<string, (string\|number\|null\|boolean\|Array\|Object)>
| {}
| Data sent to server on request. Will use `body` for: POST, PUT, PATCH and `URL query params string` for: GET, HEAD, DELETE. |
| [URLParams] | Object.<string, (string\|number\|null\|boolean\|Array\|Object)>
| {}
| URL query params string. Don't use both `data` and `URLParams` together with GET, HEAD or DELETE, as they're redundant in these cases. Pick one or the other, as they will both have the same effect. |
| [files] | File
\| Array.<File>
\| Object.<string, File>
\| Object.<string, Array.<File>>
| | Files to upload to server. Will use `file` as key if literal and `files[]` if array; if object, will use properties as keys. |
| [errorMsgNoun] | string
| ''
| Appended error message noun to global error handler. |
| [errorMsg] | string
| ''
| Error message used to global error handler. Overrides `errorMsgNoun`. |
| [globalBeforeSend] | boolean
| true
| Will this specific request use the beforeSend() hook? |
| [globalAfterSend] | boolean
| true
| Will this specific request use the afterSend() hook? |
| [defaultOptionsUsed] | boolean
| true
| Will this specific request use the default options specified on instantiation and the return value of `dynamicOptions()`? |
## RequestOptions : Object
The request object options.
**Kind**: global typedef
**Properties**
| Name | Type | Description |
| --- | --- | --- |
| [...requestOptionsNoInit] | [RequestOptionsNoInit
](#RequestOptionsNoInit) | |
| [...rest] | RequestInit
| [Init options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) from Fetch API. |
## dynamicOptionsCallback ⇒ RequestInit
Callback for global dynamic options. Allows a dynamic option to be set, like a token stored in
localStorage.
**Kind**: global typedef
**Returns**: RequestInit
- [Init options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
from Fetch API.
## beforeSendCallback : function
Callback for global before send hook.
**Kind**: global typedef
| Param | Type | Description |
| --- | --- | --- |
| [options] | Object
| |
| [options.url] | string
| The URL. |
| [options.fetchAPIOptions] | RequestInit
| [Init options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) from Fetch API. |
| [...options.requestOptions] | [RequestOptionsNoInit
](#RequestOptionsNoInit) | The request object options without Fetch API options. |
## afterSendCallback : function
Callback for global after send hook.
**Kind**: global typedef
| Param | Type | Description |
| --- | --- | --- |
| response | [ResponsePlus
](#ResponsePlus) | Request object plus responseJSON and responseText properties if correct header type. |
## errorHandlerCallback : function
Callback for global error handler.
**Kind**: global typedef
| Param | Type | Description |
| --- | --- | --- |
| [options] | Object
| |
| [options.error] | FarFetchError
\| Error
| The FarFetchError option. Will throw regular error if needed. |
| [options.response] | [ResponsePlus
](#ResponsePlus) | Request object plus responseJSON and responseText properties if correct header type. |
| [options.userMessage] | string
| The message given to the user. |
## errorMsgTemplateCallback ⇒ string
Callback for overriding default error message template.
**Kind**: global typedef
**Returns**: string
- Full error message string.
| Param | Type | Description |
| --- | --- | --- |
| [options] | Object
| |
| [options.method] | 'GET'
\| 'POST'
\| 'PUT'
\| 'PATCH'
\| 'DELETE'
\| 'HEAD'
| The CRUD method. |
| [options.errorMsgNoun] | string
| The error message noun. |