https://github.com/jalik/js-fetch-client
Instanciable and customizable fetch client for Browser and NodeJS
https://github.com/jalik/js-fetch-client
fetch http
Last synced: 10 months ago
JSON representation
Instanciable and customizable fetch client for Browser and NodeJS
- Host: GitHub
- URL: https://github.com/jalik/js-fetch-client
- Owner: jalik
- License: mit
- Created: 2023-05-04T01:19:38.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-02-26T19:56:06.000Z (about 2 years ago)
- Last Synced: 2025-05-26T22:15:33.100Z (11 months ago)
- Topics: fetch, http
- Language: TypeScript
- Homepage:
- Size: 1.36 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# @jalik/fetch-client



[](https://github.com/jalik/js-fetch-client/issues)


HTTP client based on Fetch API with error handling and other DX improvements.
## Features
* Based on Fetch API (RequestInit + Response), with extra options
* Works in the browser (use fetch polyfill for old browsers)
* Works in NodeJS (since version 18)
* Shortcut methods (DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT)
* Global configuration for all requests (headers, options and base URL)
* Conversion of response body using a type (json, blob, text, arrayBuffer...)
* Transform request options and headers before sending
* Transform response body before return
* Transform response error before return
* TypeScript declarations ♥
## Sandbox
Play with the lib here:
https://codesandbox.io/s/jalik-fetch-client-demo-8rolt2?file=/src/index.js
## Installing
```shell
npm i -P @jalik/fetch-client
```
```shell
yarn add @jalik/fetch-client
```
## Creating a client
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
```
## Executing a request
The method `.fetch(url, options)` is a generic method to execute a request.
It's like calling `fetch()` directly, but with all the benefits of using `FetchClient` (error handling, body transformations...).
Usually, you would prefer to use a shortcut method (described after) like `.get()` or `.post()` instead of `.fetch()`.
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.fetch('https://jsonplaceholder.typicode.com/todos/1', {
method: 'GET',
responseType: 'json'
})
.then((resp) => {
console.log(resp.body)
})
```
### Request options
The request options are the same as Fetch options with extra options.
```ts
type FetchOptions = RequestInit & {
/**
* The type of response to expect.
* Pass undefined to ignore response body.
*/
responseType?: ResponseType
}
```
### Response object
The response object returned by all request methods follows the declaration below.
```ts
type FetchClientResponse = {
/**
* Response body.
*/
body: T
/**
* Response headers.
*/
headers: Record
/**
* The original Fetch Response.
*/
original: Response
/**
* Tells if the request has been redirected.
*/
redirected: boolean
/**
* Response status code (ex: 200).
*/
status: number
/**
* Response status text (ex: "OK").
*/
statusText: string
/**
* Contains the response type.
*/
type: ResponseType
}
```
### Executing a DELETE request
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.delete('https://jsonplaceholder.typicode.com/posts/1')
```
### Executing a GET request
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.get('https://jsonplaceholder.typicode.com/todos/1', {
// Convert response body to JSON.
// It can be done per request, or for all requests when passed to FetchClient options.
responseType: 'json'
})
.then((resp) => {
console.log(resp.body)
})
```
### Executing a HEAD request
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.head('https://jsonplaceholder.typicode.com/todos/1')
.then((resp) => {
// Access response headers
console.log(resp.headers)
})
```
### Executing an OPTIONS request
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.options('https://jsonplaceholder.typicode.com/todos')
.then((resp) => {
// Access response headers
console.log(resp.headers)
})
```
### Executing a PATCH request
When `body` is an object and `Content-Type` is not defined in headers:
* `body` is serialized to JSON
* `Content-Type: application/json` is added to headers
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.patch(
'https://jsonplaceholder.typicode.com/todos/1',
{ completed: true },
{ responseType: 'json' }
)
.then((resp) => {
console.log(resp.body)
})
```
### Executing a POST request
When `body` is an object and `Content-Type` is not defined in headers:
* `body` is serialized to JSON
* `Content-Type: application/json` is added to headers
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.post(
'https://jsonplaceholder.typicode.com/todos',
{ title: 'test' },
{ responseType: 'json' }
)
.then((resp) => {
console.log(resp.body)
})
```
### Executing a PUT request
When `body` is an object and `Content-Type` is not defined in headers:
* `body` is serialized to JSON
* `Content-Type: application/json` is added to headers
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
client.put(
'https://jsonplaceholder.typicode.com/todos/1',
{ title: 'test' },
{ responseType: 'json' }
)
.then((resp) => {
console.log(resp.body)
})
```
## Handling errors
When the server returns an error code (4xx, 5xx...), the client throws an error.
If the server returned a body (containing error details), it can be found in `error.response.body`.
However be aware that the body is only available when `responseType` is defined in `FetchClient` options or in request options.
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient()
const invalidObject = {}
client.post('https://jsonplaceholder.typicode.com/todos', invalidObject, {
// Setting the responseType is important to convert error response body.
responseType: 'json',
})
.catch((error: FetchResponseError) => {
console.error(
// the status error
error.message,
// the server response
error.response.body
)
})
```
By default, the error contains a basic message (status text like "Bad Request").
You can use the error returned by the server like below (this will be applied to all client responses).
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient({
transformError: (error: FetchResponseError, response: FetchClientResponse) => {
// Return custom server error.
if (error.response.body?.message) {
return new FetchResponseError(error.response.body.message, response)
}
return error
},
})
client.post('https://jsonplaceholder.typicode.com/todos', invalidObject, {
// Setting the responseType is important to convert error response body.
responseType: 'json',
})
.catch((error: FetchResponseError) => {
// the error message has the same value as "error.response.body.error"
console.error(error.message)
})
```
## Configuring the client
```js
import { FetchClient } from '@jalik/fetch-client'
const client = new FetchClient({
// Do something async after each successful request (200 >= code < 400).
afterEach: async (url, resp) => {
return resp
},
// Prefix all relative URL with the base URL (does nothing on absolute URL).
baseUrl: 'http://localhost',
// Do something async before each request.
beforeEach: async (url, options) => {
return options
},
// Set default headers for all requests (empty by default).
headers: {
'authorization': '...',
'x-xsrf-token': '...',
},
// Set default Fetch options for all requests.
options: {
mode: 'cors',
},
// Enable conversion of body response.
// Use one of "arrayBuffer", "blob", "formData", "json", "stream", "text", or
// undefined to ignore response body.
responseType: 'json',
// Transform response error before returning.
transformError: (error: FetchResponseError, response: FetchClientResponse) => {
// Return custom server error.
if (error.response.body?.message) {
return new FetchResponseError(error.response.body.message, response)
}
return error
},
// Transform request options and headers before sending.
// Several functions can be passed (all executed sequentially).
transformRequest: [
(url, options) => ({
...options,
headers: {
...options.headers,
// Add request date to each request
'x-request-date': Date.now().toString(),
},
}),
],
// Transform response Body before returning.
// Several functions can be passed (all executed sequentially).
transformResponse: [
(body, response) => ({
...body,
// Add response date to each response
receivedAt: Date.now(),
}),
],
})
```
## Changelog
History of releases is in the [changelog](./CHANGELOG.md) on GitHub.
## License
The code is released under the [MIT License](http://www.opensource.org/licenses/MIT).