@alwatr/fetch
Advanced tools
Comparing version 0.16.0 to 0.17.0
@@ -6,10 +6,22 @@ # Change Log | ||
# [0.16.0](https://github.com/AliMD/alwatr/compare/v0.15.0...v0.16.0) (2022-09-08) | ||
# [0.17.0](https://github.com/AliMD/alwatr/compare/v0.16.1...v0.17.0) (2022-10-21) | ||
**Note:** Version bump only for package @alwatr/fetch | ||
### Bug Fixes | ||
- **fetch:** not ok retry condition ([0da1edd](https://github.com/AliMD/alwatr/commit/0da1edda64ec5abf70a83361d159e207691e368e)) | ||
### Features | ||
- **fetch:** docs & pattern ([459ad1c](https://github.com/AliMD/alwatr/commit/459ad1c5996f851769306639136d79c0f7270770)) | ||
- **fetch:** logger & jsdocs ([36f652d](https://github.com/AliMD/alwatr/commit/36f652d5dad23b2aeb824af77d1b0e442119c6e8)) | ||
- **fetch:** retry pattern ([5350d1a](https://github.com/AliMD/alwatr/commit/5350d1a81b9134d598f46006a1baa880b280ea98)) | ||
### Performance Improvements | ||
- **fetch:** syntax ([1fdd02e](https://github.com/AliMD/alwatr/commit/1fdd02ec8b52e32a124b8d7c1d1c7fd7c993e3af)) | ||
# [0.16.0](https://github.com/AliMD/alwatr/compare/v0.15.0...v0.16.0) (2022-09-08) | ||
**Note:** Version bump only for package @alwatr/fetch | ||
# [0.15.0](https://github.com/AliMD/alwatr/compare/v0.14.0...v0.15.0) (2022-09-01) | ||
@@ -16,0 +28,0 @@ |
@@ -8,31 +8,47 @@ declare global { | ||
/** | ||
* @default 10_000 ms | ||
* A timeout for the fetch request. | ||
* | ||
* @default 5000 ms | ||
*/ | ||
timeout?: number; | ||
bodyObject?: Record<string | number, unknown>; | ||
/** | ||
* If fetch response not acceptable or timed out, it will retry the request. | ||
* | ||
* @default 3 | ||
*/ | ||
retry?: number; | ||
bodyJson?: Record<string | number, unknown>; | ||
queryParameters?: Record<string, string | number | boolean>; | ||
} | ||
/** | ||
* Enhanced base fetch API. | ||
* @example const response = await fetch(url, {jsonResponse: false}); | ||
* It's a wrapper around the browser's `fetch` function that adds retry pattern with timeout | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* const response = await fetch(url, {timeout: 5_000, bodyJson: {a: 1, b: 2}}); | ||
* ``` | ||
*/ | ||
export declare function fetch(url: string, options?: FetchOptions): Promise<Response>; | ||
/** | ||
* Enhanced get data. | ||
* @example | ||
* const response = await postData('/api/products', {limit: 10}, {timeout: 5_000}); | ||
* It fetches a JSON file from a URL, and returns the JSON data | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* const productList = await getJson<ProductResponse>('/api/products', {queryParameters: {limit: 10}, timeout: 5_000}); | ||
* ``` | ||
*/ | ||
export declare function getData(url: string, queryParameters?: Record<string | number, string | number | boolean>, options?: FetchOptions): Promise<Response>; | ||
export declare function getJson<ResponseType extends Record<string | number, unknown>>(url: string, options?: FetchOptions): Promise<ResponseType>; | ||
/** | ||
* Enhanced fetch JSON. | ||
* @example | ||
* const productList = await getJson('/api/products', {limit: 10}, {timeout: 5_000}); | ||
* It takes a URL, a JSON object, and an optional FetchOptions object, and returns a Promise of a | ||
* Response object | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* const response = await postJson('/api/product/new', {name: 'foo', ...}); | ||
* ``` | ||
*/ | ||
export declare function getJson<ResponseType extends Record<string | number, unknown>>(url: string, queryParameters?: Record<string | number, string | number | boolean>, options?: FetchOptions): Promise<ResponseType>; | ||
/** | ||
* Enhanced post json data. | ||
* @example | ||
* const response = await postData('/api/product/new', {name: 'foo', ...}); | ||
*/ | ||
export declare function postData(url: string, body: Record<string | number, unknown>, options?: FetchOptions): Promise<Response>; | ||
export declare function postJson(url: string, bodyJson: Record<string | number, unknown>, options?: FetchOptions): Promise<Response>; | ||
//# sourceMappingURL=fetch.d.ts.map |
144
fetch.js
@@ -8,18 +8,21 @@ import { createLogger, alwatrRegisteredList } from '@alwatr/logger'; | ||
/** | ||
* Enhanced base fetch API. | ||
* @example const response = await fetch(url, {jsonResponse: false}); | ||
* It's a wrapper around the browser's `fetch` function that adds retry pattern with timeout | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* const response = await fetch(url, {timeout: 5_000, bodyJson: {a: 1, b: 2}}); | ||
* ``` | ||
*/ | ||
export function fetch(url, options) { | ||
export function fetch(url, options = {}) { | ||
logger.logMethodArgs('fetch', { url, options }); | ||
if (!navigator.onLine) { | ||
logger.accident('fetch', 'abort_signal', 'abort signal received', { url }); | ||
throw new Error('fetch_offline'); | ||
} | ||
options = { | ||
method: 'GET', | ||
timeout: 15000, | ||
window: null, | ||
...options, | ||
}; | ||
if (options.queryParameters != null) { | ||
// if (!navigator.onLine) { | ||
// logger.accident('fetch', 'abort_signal', 'abort signal received', {url}); | ||
// throw new Error('fetch_offline'); | ||
// } | ||
options.method ?? (options.method = 'GET'); | ||
options.timeout ?? (options.timeout = 5000); | ||
options.retry ?? (options.retry = 3); | ||
options.window ?? (options.window = null); | ||
if (url.lastIndexOf('?') === -1 && options.queryParameters != null) { | ||
// prettier-ignore | ||
@@ -34,4 +37,4 @@ const queryArray = Object | ||
} | ||
if (options.bodyObject != null) { | ||
options.body = JSON.stringify(options.bodyObject); | ||
if (options.body != null && options.bodyJson != null) { | ||
options.body = JSON.stringify(options.bodyJson); | ||
options.headers = { | ||
@@ -45,2 +48,8 @@ ...options.headers, | ||
const externalAbortSignal = options.signal; | ||
options.signal = abortController.signal; | ||
let timedOut = false; | ||
const timeoutId = setTimeout(() => { | ||
abortController.abort('fetch_timeout'); | ||
timedOut = true; | ||
}, options.timeout); | ||
if (externalAbortSignal != null) { | ||
@@ -50,2 +59,3 @@ // Respect external abort signal | ||
abortController.abort(`external abort signal: ${externalAbortSignal.reason}`); | ||
clearTimeout(timeoutId); | ||
}); | ||
@@ -59,44 +69,86 @@ } | ||
}); | ||
options.signal = abortController.signal; | ||
const timeoutId = setTimeout(() => abortController.abort('fetch_timeout'), options.timeout); | ||
// @TODO: browser fetch polyfill | ||
const response = window.fetch(url, options); | ||
response.then(() => clearTimeout(timeoutId)); | ||
return response; | ||
} | ||
/** | ||
* Enhanced get data. | ||
* @example | ||
* const response = await postData('/api/products', {limit: 10}, {timeout: 5_000}); | ||
*/ | ||
export function getData(url, queryParameters, options) { | ||
logger.logMethodArgs('getData', { url, queryParameters, options }); | ||
return fetch(url, { | ||
queryParameters, | ||
...options, | ||
return response | ||
.then((response) => { | ||
clearTimeout(timeoutId); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
if (response.status >= 502 && response.status <= 504 && options.retry > 1) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
options.retry--; | ||
options.signal = externalAbortSignal; | ||
logger.accident('fetch', 'fetch_nok', 'fetch not ok and retry', { | ||
retry: options.retry, | ||
response, | ||
}); | ||
return fetch(url, options); | ||
} | ||
return response; | ||
}) | ||
.catch((reason) => { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
if (timedOut && options.retry > 1) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
options.retry--; | ||
options.signal = externalAbortSignal; | ||
logger.accident('fetch', 'fetch_catch', 'fetch catch and retry', { | ||
retry: options.retry, | ||
reason, | ||
}); | ||
return fetch(url, options); | ||
} | ||
else { | ||
throw reason; | ||
} | ||
}); | ||
} | ||
/** | ||
* Enhanced fetch JSON. | ||
* @example | ||
* const productList = await getJson('/api/products', {limit: 10}, {timeout: 5_000}); | ||
* It fetches a JSON file from a URL, and returns the JSON data | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* const productList = await getJson<ProductResponse>('/api/products', {queryParameters: {limit: 10}, timeout: 5_000}); | ||
* ``` | ||
*/ | ||
export async function getJson(url, queryParameters, options) { | ||
logger.logMethodArgs('getJson', { url, queryParameters, options }); | ||
const response = await getData(url, queryParameters, options); | ||
if (!response.ok) { | ||
throw new Error('fetch_nok'); | ||
export async function getJson(url, options = {}) { | ||
logger.logMethodArgs('getJson', { url, options }); | ||
const response = await fetch(url, options); | ||
let data; | ||
try { | ||
if (!response.ok) { | ||
throw new Error('fetch_nok'); | ||
} | ||
data = (await response.json()); | ||
} | ||
return response.json(); | ||
catch (err) { | ||
logger.accident('getJson', 'response_json', 'response json error', { | ||
retry: options.retry, | ||
err, | ||
}); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
if (options.retry > 1) { | ||
data = await getJson(url, options); | ||
} | ||
else { | ||
throw err; | ||
} | ||
} | ||
return data; | ||
} | ||
/** | ||
* Enhanced post json data. | ||
* @example | ||
* const response = await postData('/api/product/new', {name: 'foo', ...}); | ||
* It takes a URL, a JSON object, and an optional FetchOptions object, and returns a Promise of a | ||
* Response object | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* const response = await postJson('/api/product/new', {name: 'foo', ...}); | ||
* ``` | ||
*/ | ||
export function postData(url, body, options) { | ||
logger.logMethodArgs('postData', { url, body, options }); | ||
export function postJson(url, bodyJson, options) { | ||
logger.logMethod('postJson'); | ||
return fetch(url, { | ||
method: 'POST', | ||
bodyObject: body, | ||
bodyJson, | ||
...options, | ||
@@ -103,0 +155,0 @@ }); |
{ | ||
"name": "@alwatr/fetch", | ||
"version": "0.16.0", | ||
"version": "0.17.0", | ||
"description": "Enhanced fetch api with timeout, helper methods and types written in tiny TypeScript module.", | ||
@@ -35,6 +35,6 @@ "keywords": [ | ||
"dependencies": { | ||
"@alwatr/logger": "^0.16.0", | ||
"@alwatr/logger": "^0.17.0", | ||
"tslib": "^2.3.1" | ||
}, | ||
"gitHead": "403b42f686edc84c4a3021b40f5b05dce0289f12" | ||
"gitHead": "f531925ca3db1072c0eaea90cb18e00802c93247" | ||
} |
@@ -9,13 +9,52 @@ # @alwatr/fetch | ||
Options have two other parameters: | ||
Options have some other parameters: | ||
- `bodyObject`: a JSON object that converts to string and put on the body. | ||
- `queryParameters`: a JSON object that converts to URL query params | ||
- `bodyJson`: a JSON object that converts to string and put on the body. | ||
- `queryParameters`: a JSON object that converts to URL query params. | ||
- `timeout`: A timeout for the fetch request. | ||
- `retry` If fetch response not acceptable or timed out, it will retry the request | ||
## Example usage | ||
```js | ||
```ts | ||
import {getJson} from 'https://esm.run/@alwatr/fetch'; | ||
const productList = await getJson('/api/products', {limit: 10}, {timeout: 5_000}); | ||
interface ProductInterface { | ||
_id: string; | ||
name: string; | ||
description: string; | ||
image: string; | ||
} | ||
const productList = await getJson<Record<string, ProductInterface>>('/api/products', { | ||
queryParameters: {limit: 10}, | ||
timeout: 15_000, | ||
retry: 5, | ||
}); | ||
``` | ||
## API | ||
### `fetch(url: string, options: FetchOptions = {})` | ||
It's a wrapper around the browser's `fetch` function that adds retry pattern with timeout | ||
```ts | ||
await fetch(url, {timeout: 5_000, bodyJson: {a: 1, b: 2}}); | ||
``` | ||
### `getJson(url: string, options: FetchOptions = {})` | ||
It fetches a JSON file from a URL, and returns the JSON data | ||
```ts | ||
await getJson('/api/products', {queryParameters: {limit: 10}, timeout: 5_000}); | ||
``` | ||
### `postJson(url: string, bodyJson: Record<string | number, unknown>, options?: FetchOptions)` | ||
It takes a URL, a JSON object, and an optional FetchOptions object, and returns a Promise of a Response object | ||
```ts | ||
await postJson(url, {first_name: 'foo', last_name: 'bar'}); | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19726
203
60
4
+ Added@alwatr/logger@0.17.0(transitive)
- Removed@alwatr/logger@0.16.0(transitive)
Updated@alwatr/logger@^0.17.0