fetch-extra v1.0.1
fetch-extra
Extra features for whatwg fetch and Request like query
object, JSON body
, timeout, abort, transformers
. Works for browser and Node.js.
Table of Contents
- Table of Contents
- Installations
- fetch
- Request
- New
Request#fetch(...options)
method - Enhanced
url
option - Enhanced
Request#clone(...options)
method - New
responseType
option - New
query
option - Enhanced
body
option - New
type
option - New
simple
option - Polyfill
AbortController
- New
queryStringify
option - New
queryParse
option - New
queryTransformer
option - New
urlTransformer
option - New
headersTransformer
option - New
bodyTransformer
option - New
responseTransformer
option - New
responseDataTransformer
option - New
errorTransformer
option
- New
- Contributing
- License
Installations
Using npm:
$ npm install fetch-extra
Or using 1998 Script Tag:
<script src="https://unpkg.com/fetch-extra@latest/dist/fetch-extra-umd.js"></script>
(Module exposed as `fetchExtra`)
To install fetch-extra with window.fetch
polyfill, please istall fetch-extra-polyfill
instead
fetch
import fetch from "fetch-extra";
(async function main() {
const url = "https://swapi.co/api/people/";
const res = await fetch(url, {
method: "POST",
type: "json" /* json Content-Type header */,
responseType: "json" /* short for `await res.json()` */,
query: { token: "asdf" } /* query object */,
body: {
/* json body object */
firstName: "Luke",
familyName: "Skywalker"
}
});
console.log(res.name); /* Luke Skywalker */
})();
Syntax
Promise\<Response> fetch(...options)
...options
\<...String|Object|Request>
- If
options
is a string, it is treated as aURL
- If
options
is a object, it is treated asRequest
options. Checkout below for detail. All fetch options are supported
Later options
will similarly overwrite earlier ones.
Description
The Fetch API provides an interface for fetching resources.
fetch
syntax adapts to WHATWG fetch:
import fetch from "fetch-extra";
(async function main() {
const url = "https://swapi.co/api/people/1/";
const res = await fetch(url, { method: "GET" });
const luke = await res.json();
console.log(luke.name); /* Luke Skywalker */
})();
But there are some extra options.
const res = await fetch({
url: "https://swapi.co/api/people/1/",
query: { page: 32 },
responseType: "json",
timeout: 30000
});
For more extra options and usages, please checkout below.
Request
import { Request } from "fetch-extra";
(async function main() {
const base = new Request({
url: "https://swapi.co/api/",
type: "json",
responseType: "json",
timeout: 30000,
async headersTransformer(headers) {
headers['access-token'] = await fakeGetToken(),
return headers;
},
});
const people = base.clone("/people");
const starships = base.clone("/starships");
const luke = await people.fetch("/1");
const c3po = await people.fetch("/2");
const starDestroyer = await starships.fetch("/3");
})();
Syntax
\<Request> new Request(...options)
...options
\<...String|Object|Request>
All options are the same with fetch(...options)
.
Description
The Request interface of the Fetch API represents a resource request.
Request syntax also adapts to WHATWG Request.
import fetch, { Request } from "fetch-extra";
(async function main() {
const url = "https://swapi.co/api/people/1/";
const request = new Request(url);
const res = await fetch(request);
const luke = await res.json();
})();
But there are some extra options and methods.
Why
fetch()
is useful, but new Request()
provides a way to inherit requests. It is recommended to create a base Request
instance to share base url, Content-Type
header, access token header, response type, error handler (by using errorTransformer()), etc, and then fetch()
or clone()
the base request.
New Request#fetch(...options)
method
A shortcut for fetch(request, ...options)
.
All options are the same with fetch(...options)
.
import { Request } from "fetch-extra";
const request = new Request(url);
const res = await request.fetch();
const luke = await res.json();
Fetching with options:
import { Request } from "fetch-extra";
const request = new Request(url);
const res = await request.fetch({ method: "DELETE" });
const luke = await res.json();
The example above is equal to:
import fetch, { Request } from "fetch-extra";
const request = new Request(url);
const res = await fetch(request, { method: "DELETE" });
const luke = await res.json();
Enhanced url
option
URLs
could be composed.
const baseUrl = "https://swapi.co/api/";
const swRequest = new Request(baseUrl);
const lukeRes = await swRequest.fetch("/people/1/");
/* final URL will be "https://swapi.co/api/people/1/" */
const starShipRes = await swRequest.fetch("/starships/9/");
/* final URL will be "https://swapi.co/api/starships/9/" */
To override earlier URL, just give a new URL starts with a protocol (like http://
or https://
):
const swRequest = new Request("https://swapi.co/", options);
const pokeRes = swRequest.fetch("https://pokeapi.co/api/v2/");
/* final URL will be "https://pokeapi.co/api/v2/" */
Enhanced Request#clone(...options)
method
const baseRequest = new Request({
headers: { "Content-Type": "application/json" }
});
const swRequest = baseRequest.clone("https://swapi.co/api/");
const luke = await swRequest.fetch("/people/1/");
const c3po = await swRequest.fetch("/people/2/");
const pokeRequest = baseRequest.clone("https://pokeapi.co/api/v2/");
const bulbasaur = await pokeRequest.fetch("/pokemon/1/");
The ...options
usages are the same with fetch(...options)
and new Request(...options)
New responseType
option
Returning resolved data with specified type instead of response
object.
const options = { responseType: "json" };
const luke = await swRequest.fetch(
options
); /* <-- no need `await res.json()` */
console.log(luke.name); /* Luke Skywalker */
In browser, responseType
value could be one of arrayBuffer
, blob
, formData
, json
or text
.
In Node.js, formData
is NOT supported.
If responseType
is none
, it will return the original response
object.
New query
option
const results = await swRequest.fetch({ query: { search: "luke" } });
/* final URL will be "https://swapi.co/api/people?search=luke" */
query
could be JSON object or string (like name=luke&height=172
).
If url
has search fields (like https://swapi.co/api/people?search=luke
), query string will append to the search fields.
Enhanced body
option
const results = await swRequest.fetch({
method: "POST",
body: { name: "Luke Skywalker" } /* <-- could be a JSON */
});
/* final body will be '{"name":"Luke Skywalker"}' */
New type
option
const results = await swRequest.fetch({
method: 'POST',
type: 'form'
body: { name: 'Luke Skywalker' },
});
/* final body will be 'name=Luke%20Skywalker' */
/* final header['Content-Type'] will be 'application/x-www-form-urlencoded' */
type
value will auto set to headers Content-Type
.
Value form
is short for application/x-www-form-urlencoded
, and json
is short for application/json
.
New simple
option
Will throw error if response
status is non-2xx.
await swRequest
.fetch({
simple: true,
url: "/400" /* simulate response with 400 HTTP status */
})
.catch(err => {
console.error(err); /* <-- Error: Bad Request */
});
Polyfill AbortController
Built-in AbortController and AbortSignal polyfill for aborting fetch.
import fetch, { AbortController } from "fetch-extra";
(async function main() {
const abortController = new AbortController();
const fetchPromise = fetch("https://swapi.co/api/people/1", {
signal: abortController.signal
});
abortController.abort();
await fetchPromise.catch(err => {
if (err.name === "AbortError") console.warn("Aborted.");
else console.error(err.message);
});
})();
New queryStringify
option
Setting a custom function in charge of serializing query
object.
import qs from "qs";
const request = new Request({
queryStringify: qs.stringify
});
By default, this function is tiny-querystring stringify
function.
New queryParse
option
Setting a custom function in charge of parsing query
string.
import qs from "qs";
const request = new Request({
queryParse: qs.parse
});
By default, this function is tiny-querystring parse
function.
New queryTransformer
option
Setting a function to transform query
object, should return a new query
object. Will be called before fetching.
const baseRequest = new Request({
queryTransformer: (query) => { /* <-- queryTransformer */
query.accessToken = '<ACCESS_TOKEN>',
return query;
},
});
const swRequest = baseRequest.clone('https://swapi.co/api/');
const results = await swRequest.fetch('/people', {
query: { search: 'luke' },
});
/* final URL will be "https://swapi.co/api/people?search=luke&accessToken=<ACCESS_TOKEN>" */
All transformers could return promises.
const baseRequest = new Request({
async queryTransformer(query) { /* <-- async queryTransformer */
query.accessToken = await getTokenAsync(),
return query;
},
});
/* ... */
New urlTransformer
option
Like queryTransformer
, but transform url
.
New headersTransformer
option
Like queryTransformer
, but transform headers
.
New bodyTransformer
option
Like queryTransformer
, but transform body
.
New responseTransformer
option
Transform response instance.
const baseRequest = new Request({
responseType: "json",
responseTransformer(response) {
/* <-- responseTransformer */
if (response.status === 404) {
throw new Error("Page not found");
}
return response;
}
});
/* ... */
New responseDataTransformer
option
Like responseTransformer
, but transform the data after responseType
resolved.
const baseRequest = new Request({
responseType: "json",
responseDataTransformer(json) {
/* <-- responseDataTransformer */
if (json) {
json.fullName = `${json.firstName} ${json.familyName}`;
}
return json;
}
});
/* ... */
New errorTransformer
option
Transform error or rejection.
const baseRequest = new Request({
errorTransformer(error) {
/* <-- errorTransformer */
if (error.name === "Abort") {
console.warn("Fetch aborted");
}
return error;
}
});
/* ... */
Contributing
Please checkout CONTRIBUTING.md
License
MIT