Fetchi
fetch wrapper client for the browser and applications
Table of Contents
- Why Using Fetchi
- Installatoin
- Simple Examples
- Fetchi Controls
- Config
- Mocking Response
- TypeScript
- Suger Codes
Why use fetchi
Fetchi is a fetch wrapper utility wrriten 100% in TypeScript, in order to bring more functionalities like Canceling
, Retring
, intercepting
, better error handling
and easy API Mocking
. because of using typescript, it ables you to handle everything type safe.
Installing
Using npm:
$ npm install fetchi-request
Using yarn:
$ yarn add fetchi-request
Once the package is installed, you can import the library using import
or require
approach:
import fetchi from 'fetchi-request';
Example
import fetchi, { FetchiError, FetchReseponse } from 'fetchi-request';
// Make a request for a user with a given ID
fetchi<Response>({url: '/user?ID=12345'})
.then((response) => {
// handle success
console.log(response);
})
.catch((error: FetchiError) => {
// handle error
console.log(error);
})
.finally(() => {
// always executed
});
// Optionally the request above could also be done as
fetchi<User>({ url: '/user' }, params: { ID: 12345 })
.then((response) => {
console.log(response);
})
.catch((error: FetchiError) => {
console.log(error);
})
.finally(function () {
// always executed
});
// you can also see the full response (if the response is success)
fetchi<User>({ url: '/user' }, params: { ID: 12345 })
.fullResponse((result: FetchReseponse<User>) => {
// response here contains status, and the comprehensive configurations information of the request
return {
response: result.response,
status: result.status,
config: result.config
}
}).then((comprehensiveResponse: FetchResponse<User>) => {
console.log(comprehensiveResponse)
})
// you can use these http verbs "GET" (default), "POST", "PUT", "DELETE"
fetchi<User>({ url: '/user/12345' }, method: "PUT", params: { username: "myUsername" })
Controls
With Fetchi you can have more control over your asyncronous actions (for requests).
const request = fetchi<Response>({ url: '/someapi' })
// cancel the request and ignore the response (neither success nor failure would be called)
// it can be really useful when you don't need the response any more
request.cancel()
// retry the request, if the response was pending, it would be canceld and do the request again,
// all the listeners to the request would be called after the request is done
request.retry()
Configurations
Here is a comprehensive configuration options sample:
Global Configurations
// optionally you can set a base url for all of the requests (mutable).
fetchi.global.config.baseUrl = "https://your-domain.com"
// set default timeout config for all of the requests
fetchi.global.config.timeout = 5000
// mutate the default header for all requests (useful for authorization)
fetchi.global.config.headers.set('Authorization', "the-token")
// intercept response before deliver the response to the listeners or intercept the request before dooing the request
fetchi.global.config.interceptors.response = (result, request) => {
if (result.status == 401) {
fetchi.global.config.headers.set('Authorization', "another-token")
request.retry() // you can even retry the request it would cancel the request first (ignore the current response) and do it again.
}
// for example all the Apis return the target response type in the data hierarchy
return result.response.data; // mutates all the responses
}
// change the default validation checking for the responses (checking status)
fetchi.global.config.validateStatus = (status: number) => status >= 200 && status < 300
// check if any request is pending or not
fetchi.global.config.onPendingRequestsChanged = (isPending: boolean, numberOfRequests: number) => {
// e.g.
globalLoadingIndicator.isActive = isPending;
};
// you can see retry config for all of the requests!! (how many time retry and what should be the delay between them)
fetchi.global.config.retryConfig = {
count: number;
delay: number; // millisecnods
};
Request Configurations
fetchi({
url: '/your-end-point', // required value
method: 'POST', // optional "GET" | "POST" " | "PUT" | "DELETE"
params: { something: 'someValue' }, // query param (for GET requests), and body request for POST & PUT & DELETE requests
cachePolicy: 'default' // Optional 'default' | 'no-cache' | 'reload' | 'force-cache' | 'only-if-cached';
timeout: 4000, // timeout for this specific request (overwrite the global one)
timeoutErrorMessage: "Timeout Message", // Optional timeout error message
headers: { "Authorization": "myToken" }, // Optional Header option, it would be merged with the global config's header
retryConfig: { // retry configuration for this request
count: 2,
delay: 1000 // millisecnods
},
validateStatus: (status: number) => status < 400, // validation logic for this specific request (status check)
onPendingStatusChanged: (isLoading) => { console.log('isLoading', isLoading) } // listener for pending state of this request
})
There are some other options for configurations but I prefered to seperate them in Mocking Response topic.
Mocking
There are two ways for mocking the response of requests:
Globaly
This approach can be used when you are going to centeralized all the server (mocking) functionality is one place and use it for all requests.
import fetchi, { Adaptor, Config, FetchResponse, FetchiError } from 'fetchi-request';
class MockAdaptor implements Adaptor {
request<T>(config: Config): Promise<FetchResponse<T>> {
if (config.url === '/user') {
return Promise.resolve({
response: {
name: 'myName',
lastName: 'myLastName'
} as T,
status: 200,
config,
});
}
throw new FetchiError({
data: Error('Invalid Url'),
status: 800,
config,
});
}
cancel() {}
}
fetchi.global.config.mockAdaptor = new MockAdaptor();
After configuration of mockAdaptor, you can tell to all requests to use the mock adaptor or tell each request manually to use the mock adaptor:
// all requests use mock adaptor
fetchi.global.config.useMock = true
// or set it (ON or OFF) manually for each request (it would override the global setting)
fetchi({
url: "/user",
useMock: true
})
Mcok Single Request
if you need to mock the response of only one request you can pass a mock adaptor to config of the request:
fetchi({
url: "/user",
mockAdaptor: new MockAdaptor(),
useMock: true
})
TypeScript
There are some useful types defined in this library like below:
### FetchResponse
You can see this type in the response of the requests by using `fullResponse` or `rawPromise` functions. This type contains `status`, the request's `config`, and the body `response` of the request
### FetchError
It's almost the same as the FetchResponse type, but it's not only a type, it's an object which you can receive it in the errors or create it manually (for mock adaptors)
throw new FetchiError({
data: Error('My Custom Error'),
status: 800,
config,
});
AnyAsyncService (the most useful one)
This is one of the most useful type that you can use, in order to hide the interface of Fetchi you can type erase it with `AnyAsyncService` type: For example if you want to hide the logic from the other part of your application, that you're fetching the data from server (or DataBase or location service or ....) you can use `AnyAsyncService` as interface for the other part of your application.
const login = (credentials: Credentials): AnyAsyncService<User> =>
fetchi<User>({
url: '/login',
method: 'POST',
params: { user: credentials },
})
Suger Codes (handy codes)
Just like promise object you also can do such thing:
fetchi.resolve(myObject) // it will return a response with 200 status, and `no url` for the configuration
fetchi.reject(myCustomError) // it will throw FetchiError with custom data
let req = fetchi.all([
fetchi({ url: '/first'}),
fetchi({ url: '/second'})
])
req.promise.then(([ firstRawResponse, secondRawResponse ]) => {
// do something
})
// more controls
req.cancel()
req.retry()
// race condition of two fetchi request, the second one would be canceled (ignored)
let req = fetchi.race([
fetchi({ url: '/first'}),
fetchi({ url: '/second'})
])
req.promise.then((rawResponse: FetchResponse<Something>) => {
// do something
})
// more controls
req.cancel()
req.retry()