@argab/request-service
v3.3.0
Published
Provides a fully customizable Library for handling API using Request repositories and Stubs.
Downloads
239
Maintainers
Readme
Request Service
Provides a fully customizable Library for handling API using Request Repositories and Stubs.
Initialisation
- Create your own ES-6 classes implementing "RequestClient" and "RequestHandler" Interfaces from the package.
//.......... ApiClient.js
import {RequestClient} from '@argab/request-service'
import axios from 'axios'
const _axios = (config) => axios.create(config)
export default class extends RequestClient {
get({headers, uri, params}) {
return _axios({headers}).get(uri, params)
}
post({headers, uri, params}) {
return _axios({headers}).post(uri, params)
}
patch({headers, uri, params}) {
return _axios({headers}).patch(uri, params)
}
put({headers, uri, params}) {
return _axios({headers}).put(uri, params)
}
delete({headers, uri, params}) {
return _axios({headers}).delete(uri, params)
}
}
//........... ApiHandler.js
import {RequestHandler} from '@argab/request-service'
export default class extends RequestHandler {
/*
* response checking method
* method executes within a Promise.prototype.then()
* @param: {Object} incoming response data
* @return: Boolean
* */
isSuccess(response) {
}
/*
* response checking method
* method executes within a Promise.prototype.then()
* @param: {Object} incoming response data
* @return: Boolean
* */
isError(response) {
}
/*
* method executes within a Promise.prototype.then()
* @param: {Object} incoming response data
* @return: void
* */
onSuccess(response) {
}
/*
* method executes within a Promise.prototype.then()
* @param: {Object} incoming response data
* @return: void
* */
onError(response) {
}
/*
* method executes within a Promise.prototype.catch()
* @param: {Object} error
* @return: void
* */
onCatch(error) {
}
/*
* method executes within a Promise.prototype.finally()
* @param: {Object} request data
* @return: void
* */
onFinally(data) {
}
/*
* method executes before request sent
* @param: {Object} request data
* @return: void
* */
before(data) {
}
/*
* method executes at the start of a Promise.prototype.then()
* @param: {Object} response
* @return: void
* */
after(response) {
}
/*
* method executes at the start of a Promise.prototype.catch()
* @param: {Object} error
* @return: void
* */
afterCatch(error) {
}
/*
* method executes at the start of a Promise.prototype.finally()
* @param: {Object} request data
* @return: void
* */
afterFinally(data) {
}
}
- Initialise the Request Service and connect it to your App:
import {RequestService} from '@argab/request-service'
import ApiClient from './src/api/ApiClient'
import ApiHandler from './src/api/ApiHandler'
MyApp.prototype.request = new RequestService({
config: {
client: ApiClient,
handler: ApiHandler
}
})
new MyApp()
Simple request
import ApiHandler from './src/api/ApiHandler'
import SomeHandler from './src/api/SomeHandler'
import SomeClient from './src/api/SomeClient'
this.request
// Configuring (optional):
.config({
handler: [SomeHandler, ApiHandler],
client: SomeClient,
headers: {'Content-Type': 'application/json;charset=UTF-8'},
log: false // (TRUE as a default) Use this flag to enable/disable
// the request logging on the main Request instance;
// The requests log may be retrieved from the main instance
// by calling "getLog" method (f.e: this.request.getLog())
})
// Mediators (optional):
.unlog() // Use this flag to disable the request logging
.headers({'Content-Type': 'application/json;charset=UTF-8'})
.json() // Similar to .headers({'Content-Type': 'application/json;charset=UTF-8'})
.encode() // Similar to .headers({'Content-Type': 'application/x-www-form-urlencoded'})
.form() // Similar to .headers({'Content-Type': 'multipart/form-data'})
.html() // Similar to .headers({'Accept': 'text/html'})
.stream() // Similar to .headers({'Content-Type': 'application/octet-stream'})
// Client Decorator (required):
.post('http://some.url', {parameters})
// Request Decorators (optional):
.success(response => {}) // or .then(...)
.error(response => {}) // If not called, then ApiHandler`s "onError" will be executed.
.catch(error => {}) // If not called, then ApiHandler`s "onCatch" will be executed.
.finally(requestData => {}) // If not called, then ApiHandler`s "onFinally" will be executed.
Request Repositories And Stubs
Request repositories allow you to move the complex logic of creating requests to a separate namespace (directory)
//............ MyApp.js
import {RequestService} from '@argab/request-service'
import ApiClient from './src/api/_clients/ApiClient'
import ApiHandler from './src/api/_handlers/ApiHandler'
MyApp.prototype.request = new RequestService({
getRepo: (path) => {
const Repo = require(`./api/${path}`).default
return new Repo
},
getStub: (path) => {
const Stub = require(`./api/_stubs/${path}`).default
return new Stub
},
useStubs: true,
config: {
client: ApiClient,
handler: ApiHandler
}
})
new MyApp()
//........... /api/orders.js
import {RequestRepository} from '@argab/request-service'
export default class extends RequestRepository {
getOrders(params) {
return this.client.get('http://some.url/orders', params)
}
}
//........... /api/_stubs/orders.js
import {RequestRepository} from '@argab/request-service'
export default class extends RequestRepository {
getOrders(params) {
return this.client.stubData({SOME_STUB_DATA}).get('/orders', params)
}
}
//.......... getOrders.js
// if "useStubs" set to true, then stubs reposiory will be connected first (only if exists).
this.request.repo('orders').getOrders({params}).then(...).error(...)
// Anyway, calling .stub() obviously will connect the stubs reposiory even if "useStubs" set to false.
this.request.stub('orders').getOrders({params}).then(...).error(...)
Use of Loaders
The "Loader" meaning is a Tool that indicates a live cycle of the request. For example a Loading Spinner in the HTML Document.
//............ MyLoader.js
import {RequestLoader} from '@argab/request-service'
class MyLoader extends RequestLoader {
_data
/*
* @property: Displays a number of requests
* that uses Loader and having pending status
* */
pending
constructor(data) {
super()
this._data = data
}
start() {
console.log('Requests in pending status: ', this.pending)
console.log('Request pending: ', `${this._data.method.toUpperCase()} ${this._data.uri}...`)
}
end() {
console.log('Request complete: ', `${this._data.method.toUpperCase()} ${this._data.uri}.`)
}
}
//............ MyApp.js
import {RequestService} from '@argab/request-service'
import ApiClient from './src/api/_clients/ApiClient'
import MyLoader from './src/api/_loaders/MyLoader'
import MyCustomLoader from './src/api/_loaders/MyCustomLoader'
MyApp.prototype.request = new RequestService({
config: {
client: ApiClient,
loader: MyLoader,
useLoader: true // Loaders switcher
}
})
const app = new MyApp()
app.request
// Loader switchers (optional):
.config({loader: MyCustomLoader}) // Setting any custom Loader for the request
.useLoader() // Boolean true as a default
.bg() // Similar to .useLoader(false)
.get('http://some.url').success(...).error(...).catch(...).finally(...)
Extending Request And RequestMiddleware
By Passing argument named "extend" allows to inject any custom pre-request and post-request handling methods to the Base Request's prototype.
//............ MyApp.js
import {RequestService} from '@argab/request-service'
import ApiClient from './src/api/_clients/ApiClient'
import ApiHandler from './src/api/_handlers/ApiHandler'
MyApp.prototype.request = new RequestService({
config: {
client: ApiClient,
handler: ApiHandler
},
extend: {
middleware: {
// Be aware to declare via "function" as it allows to access to the current context by "this".
awesome: function () {
console.log('This is my awesome middleware function!')
this.config({headers: {'X-AwesomeHeader': 'v1.0.0'}})
}
},
request: {
done: function (messageOnSuccess) {
console.log(`This is what my ApiHandler would notify about at response success: `, messageOnSuccess)
this.data.done = messageOnSuccess
},
alert: function (messageOnError) {
console.log(`This is what my ApiHandler would notify about at response error: `, messageOnError)
this.data.alert = messageOnError
}
}
}
})
//........... ApiHandler.js
import {RequestHandler} from '@argab/request-service'
export default class extends RequestHandler {
_data
constructor(data) {
super()
this._data = data
}
isSuccess(response) {
return true
}
after(response) {
const notifySuccess = this._data.done;
const notifyError = this._data.alert;
if (this.isSuccess(response) && notifySuccess) {
console.log(notifySuccess)
} else if (this.isError(response) && notifyError) {
console.error(notifyError)
}
}
}
//////////////////////////
const app = new MyApp()
app.request.awesome().get('/posts').done('Wow, that`s awesome!').alert('Ooops...')
Output from tests:
This is my awesome middleware function!
Requests in pending status: 1
Request pending: GET /posts...
This is what my ApiHandler would notify about at response success: Wow, that`s awesome!
This is what my ApiHandler would notify about at response error: Ooops...
Wow, that`s awesome!
Request complete: GET /posts.
Retrying the Request
Request retrying available both from the Request Handler and Request call:
/////////// Retrying from Handler:
import {RequestHandler} from '@argab/request-service'
export default class extends RequestHandler {
onFinally() {
/*
* (This method is Abstract (Not subject to redeclare))
* The request restarting method.
* @param: {Boolean}|{Function}(data):<Boolean|Promise>
* - Boolean or a Function returning both Boolean or a Promise
* returning Boolean that whenever
* is TRUE then restarts the request.
* @return: void
* */
this.retry((data) => new Promise(resolve => {
//...do some logic and restart the request:
resolve(true)
}))
// ANOTHER WAYS:
// getting restart at once:
this.retry(true)
// getting restart with some modifications:
this.retry(data => {
// changing the original request (optional):
data.retryChain = ({chain}) => {
chain.find(c => c.method === 'error').args[0] = () => {
console.log('"error" method has been overriden.')
}
return chain
}
//...do some logic and restart the request:
return true
})
}
}
/////////// Retrying from the request call:
const result = await app.request
.awesome()
.get('/posts')
.error(() => { throw 'Oops!' })
.retryMaxCount(3)
.retryTimeout(3000)
// .retry(true)
.retryChain(({chain}) => {
chain.find(c => c.method === 'error').args[0] = () => {
console.log('"error" method has been overriden.')
}
return chain
})
.retryOnCatch(data => {
return new Promise(res => {
setTimeout(() => {
console.error('Awesome error: ', data.dataError)
res(true)
}, 3000)
})
})
console.log(result)
Retry Methods:
/*
* This method doesn't restarts the request,
* it brings the Function that
* takes the request data as an argument
* and initiates the request restarting method.
* The Function executes at the end of a Promise.prototype.finally()
* @param: {Boolean}|{Function}(data):<Boolean|Promise>
* - Boolean or a Function returning both Boolean or a Promise
* returning Boolean that whenever is TRUE then restarts the request
* @return: void
* */
retry (resolve) {}
/*
* Restarts the request on request errors.
* The Function executes at the end of a Promise.prototype.finally()
* @param: {Boolean}|{Function}(data):<Boolean|Promise>
* - Boolean or a Function returning both Boolean or a Promise
* returning Boolean that whenever is TRUE then restarts the request
* @return: void
* */
retryOnCatch (resolve) {}
/*
* Overrides the current request`s methods call chain on retry.
* The Function executes within a retry method.
* @example: ....retryChain(({set}) => set.json()
* .post('http://some.url', {params})
* .success(...).error(...).catch(...))
* @param: {Function}({set, chain, data}):<void|Array>
* - @property "set" creates a new set
* of the current request`s methods call chain.
* - @property "chain" provides an Array
* [{method, args}, {method, args}, ....]
* containing the current request`s methods call chain.
* - @property "data" - current request`s data.
* @return: void
* */
retryChain (callback) {}
/*
* Sets a max number of retry attempts
* @param: {Number}
* @return: void
* */
retryMaxCount (count) {}
/*
* Sets the timeout in miliseconds between retry attempts
* @param: {Number}
* @return: void
* */
retryTimeout (miliseconds) {}