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

@gis-ag/oniyi-http-client

v2.1.0

Published

Adding a plugin interface to "request" that allows modifications of request parameters and response data

Downloads

5

Readme

This is a fork from oniyi-http-client

Oniyi Http Client

Adding a plugin interface to request that allows modifications of request parameters and response data

Installation

$ npm install --save @gis-ag/oniyi-http-client

Usage

Use default instance

const httpClient = require('@gis-ag/oniyi-http-client');
httpClient.get('http://httpbin.org/headers', {}, (err, response, body) => {
 if (err) {
   // handle error here
   return;
 }
 // do something with response and / or body
});

with request defaults

const httpClient = require('@gis-ag/oniyi-http-client');
const customClient = httpClient.create({
 defaults: {
   headers: {
     'custom': 'foo',
   },
   json: true,
 }
});

customClient.get('http://httpbin.org/headers', {}, (err, response, body) => {
 if (err) {
   // handle error here
   return;
 }
 console.log(body.headers.custom)
 // will log `foo`
});

with custom phases

const httpClient = require('@gis-ag/oniyi-http-client');
const customClient = httpClient.create({
 requestPhases: ['early', 'initial', 'middle', 'final'],
 responsePhases: ['initial', 'middle', 'final', 'end'],
});

Motivation

"Is there really a need for another http library?" you might ask. There isn't. The actual need is for the ability to asynchronously hook into the process of making a http request or receiving a response.

I came across this requirement when working with various REST APIs, making requests with a number of different credentials (representing users logged into my application). Since the flow within my app provided me with an user object that has an async method to retrieve this user's credentials (e.g. an oauth access-token), I wanted to follow the DRY (don't repeat yourself) pattern and not manually resolve before invoking e.g. request.

Instead I thought it would be much easier to pass the user along with the request options and have some other module take care of resolving and injecting credentials.

Quickly more use-cases come to mind:

Also, use-cases that require to manipulate some options based on other options (maybe even compiled by another plugin) can be solved by this phased implementation. Some REST APIs change the resource path depending on the type of credentials being used. E.g. when using BASIC credentials, a path might be /api/basic/foo while when using oauth the path changes to /api/oauth/foo. This can be accomplished by using e.g. oniyi-http-plugin-format-url-template in a late phase (final) of the onRequest PhaseLists.

Phases

This HTTP Client supports running multiple plugins / hooks in different phases before making a request as well as after receiving a response. Both PhaseLists are initiated with the phases initial and final and zipMerged with params.requestPhases and params.responsePhases respectively. That means you can add more phases by providing them in the factory params.

with custom phases

const httpClient = require('@gis-ag/oniyi-http-client');
const customClient = httpClient.create({
requestPhases: ['early', 'initial', 'middle', 'final'],
responsePhases: ['initial', 'middle', 'final', 'end'],
});

onRequest

onRequest is one of the (currently) two hooks that executes registered plugins in the defined phases. After all phases have run their handlers successfully, the resulting request options from ctx.options are used to initiate a new request.Request. The return value from request.Request (a readable and writable stream) is what the returned Promise from any of the request initiating methods from client (makeRequest, get, put, post, ...) resolves to.

Handlers in this phaseList must comply with PluginHookHandler. The received context argument is an OnRequestContext .

onResponse

onResponse is the second hook and executes registered plugins after receiving the response from request but before invoking callback from the request execution. That means plugins using this hook / phases can work with and modify err, response, body before the app's callback function is invoked. Here you can do things like validating response's statusCode, parsing response data (e.g. xml to json), caching, reading set-cookie headers and persist in async cookie jars... the possibilities are wide.

Handlers in this phaseList must comply with PluginHookHandler. The received context argument is an OnResponseContext.

Using plugins

Every plugin can register any number of handlers for any of the phases available onRequest as well as onResponse.

The following example creates a plugin named plugin-2 which adds a request-header with name and value plugin-2. Also, it stores some data in shared state that is re-read on response and printed.

const plugin2 = {
  name: 'plugin-2',
  onRequest: [{
    phaseName: 'initial',
    handler: (ctx, next) => {
      const { options, hookState } = ctx;
      // store something in the state shared across all hooks for this request
      _.set(hookState, 'plugin-2.name', 'Bam Bam!');

      setTimeout(() => {
        _.set(options, 'headers.plugin-2', 'plugin-2');
        next();
      }, 500);
    },
  }],
  onResponse: [{
    phaseName: 'final',
    handler: (ctx, next) => {
      const { hookState } = ctx;
      // read value from state again
      const name = _.get(hookState, 'plugin-2.name');

      setTimeout(() => {
        logger.info('Name in this plugin\'s store: %s', name);
        next();
      }, 500);
    },
  }],
};

client
  .use(plugin2)
  .get('http://httpbin.org/headers', (err, response, body) => {
    if (err) {
      logger.warn('got an error');
      if (err.stack) {
        logger.error(err.stack);
      } else {
        logger.error(err);
      }
      process.exit(0);
    }
    if (response) {
      logger.debug('statusCode: %d', response.statusCode);
      logger.debug('headers: ', response.headers);
      logger.debug('body: ', body);
    }
    process.exit(0);
  });

API

oniyi-http-client : HttpClient

The default HttpClient instance. Can be used without any further configuration

Example (Use default instance)

const httpClient = require('@gis-ag/oniyi-http-client');
httpClient.get('http://httpbin.org/headers', {}, (err, response, body) => {
 if (err) {
   // handle error here
   return;
 }
 // do something with response and / or body
});

oniyi-http-client~create([options]) ⇒ HttpClient

Create a new HttpClient instance. Use this method to create your own client instances and mount plugins for your specific request scenarios

Kind: inner method of oniyi-http-client
Returns: HttpClient - The newly created HttpClient instance

| Param | Type | Default | Description | | --- | --- | --- | --- | | [options] | Object | {} | | | [options.defaults] | Object | | default request options for the new instance. Will be merged into options provided with each request via _.defaultsDeep() | | [options.requestPhases] | Array. | | complete list of phase names for the onRequest phaseList. must include the names initial and final | | [options.responsePhases] | Array. | | complete list of phase names for the onResponse phaseList. must include the names initial and final |

Example (with request defaults)

const httpClient = require('@gis-ag/oniyi-http-client');
const customClient = httpClient.create({
 defaults: {
   headers: {
     'custom': 'foo',
   },
   json: true,
 }
});

customClient.get('http://httpbin.org/headers', {}, (err, response, body) => {
 if (err) {
   // handle error here
   return;
 }
 console.log(body.headers.custom)
 // will log `foo`
});

Example (with custom phases)

const httpClient = require('@gis-ag/oniyi-http-client');
const customClient = httpClient.create({
 requestPhases: ['early', 'initial', 'middle', 'final'],
 responsePhases: ['initial', 'middle', 'final', 'end'],
});

HttpClient

Kind: global class

httpClient.#defaults() ⇒ Object

Kind: instance method of HttpClient
Returns: Object - a clone of this instance's defaults object

httpClient.#jar([store]) ⇒ Object

Create a new CookieJar with the provided Store implementation. Will use request.jar(store) method for creation when store is not async, tough.CookieJar(store) instead.

Kind: instance method of HttpClient
Returns: Object - CookieJar

| Param | Type | Description | | --- | --- | --- | | [store] | Object | tough-cookie Store |

httpClient.#use(plugin, [options]) ⇒ Object

Kind: instance method of HttpClient
Returns: Object - a clone of this instance's defaults object

| Param | Type | | --- | --- | | plugin | Object | | plugin.name | String | | [plugin.onRequest] | Array. | | [plugin.onResponse] | Array. | | [options] | Object |

httpClient.#makeRequest(uri, [options], [callback]) ⇒ RequestPromise

make a http request with the provided arguments. Request arguments are parsed and compiled to one options object, merged with this instance's defaults. Then, the onRequest phaseList is onvoked with mentioned options as well as a hookState. After all PluginHookHandler have completed, the options from OnRequestContext are used to invoke request. The result is used to resolve this method's returned RequestPromise. This is useful if you want to work with request's' Streaming API. After a response is received, a OnResponseContext is created and passed through the onResponse phaseList before finally your provided RequestArgCallback is invoked.

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

httpClient.#get(uri, [options], [callback]) ⇒ RequestPromise

Same as #makeRequest but forces options.method to GET

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

httpClient.#put(uri, [options], [callback]) ⇒ RequestPromise

Same as #makeRequest but forces options.method to PUT

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

httpClient.#post(uri, [options], [callback]) ⇒ RequestPromise

Same as #makeRequest but forces options.method to POST

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

httpClient.#del(uri, [options], [callback]) ⇒ RequestPromise

Same as #makeRequest but forces options.method to DELETE

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

httpClient.#head(uri, [options], [callback]) ⇒ RequestPromise

Same as #makeRequest but forces options.method to HEAD

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

httpClient.#options(uri, [options], [callback]) ⇒ RequestPromise

Same as #makeRequest but forces options.method to OPTIONS

Kind: instance method of HttpClient

| Param | Type | | --- | --- | | uri | RequestArgUri | | [options] | RequestArgOptions | | [callback] | RequestArgCallback |

Type Definitions

PluginHook : Object

Kind: global typedef
Properties

| Name | Type | Description | | --- | --- | --- | | phaseName | string | Name of the phase that handler should be executed in. value can include pseudo-phase postfix ':before' or ':after' (e.g. 'initial:after' where 'initial' is the actual phaseName and ':after' the pseudo phase) | | handler | PluginHookHandler | handler function that is invoked when running through the according phase |

PluginHookHandler : function

Kind: global typedef

| Param | Type | Description | | --- | --- | --- | | context | Object | An object with the currently available request context. Hooks in the onRequest phaseList receive an OnRequestContext while hooks that run in the onResponse phaseList receive an OnResponseContext | | next | function | callback function that must be invoked once the handler function completed it's operations |

Hookstate : Object

A Hookstate instance is created for each request and shared across all phases in the onRequest and onResponse phaseLists. PluginHookHandler can modify this plain object at will (e.g. persist a timestamp in an onRequest phase and read it again in another handler in an onResponse phase)

Kind: global typedef

OnRequestContext : Object

mutable context object that gets passed through all phases in the onRequest phaseList

Kind: global typedef
Properties

| Name | Type | Description | | --- | --- | --- | | hookState | Hookstate | | | options | Object | request options |

OnResponseContext : Object

mutable context object that gets passed through all phases in the onResponse phaseList

Kind: global typedef
Properties

| Name | Type | Description | | --- | --- | --- | | hookState | Hookstate | this is the Hookstate instance from this request's OnRequestContext | | options | Object | the options property frtom this request's OnRequestContext (request options) | | requestError | Error | an error when applicable (usually from (http.ClientRequest) object) | | responseBody | Object | the response body (String or Buffer, or JSON object if the json option is supplied) | | response | Object | an http.IncomingMessage (http.IncomingMessage) object (Response object) |

RequestArgUri : String ⎮ Object

The first argument can be either a url or an options object. The only required option is uri; all others are optional.

Kind: global typedef

RequestArgOptions : Object ⎮ function

The sesond argument can bei either options object or callback function.

Kind: global typedef

RequestArgCallback : function

Callback function, Invoked at the end of response receiving (or in case of error, when the error is generated). Receives three arguments (err, response, body) that are also available in OnResponseContext

Kind: global typedef

RequestPromise : Promise

A Promise that resolves to the return value for request() after all PluginHookHandler in the onRequest phaseList have completed. If any of the PluginHookHandlers produces an error, Promise is rejected with that error object.

Kind: global typedef

License

MIT © Benjamin Kroeger