npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@argab/request-service

v3.3.0

Published

Provides a fully customizable Library for handling API using Request repositories and Stubs.

Downloads

239

Readme

Request Service

Provides a fully customizable Library for handling API using Request Repositories and Stubs.

Initialisation

  1. 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) {
    }
}
  1. 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) {}