This is a fork from oniyi-http-client
with request defaults
with custom phases
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
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
with custom phases ```js const httpClient = require('@gis-ag/oniyi-http-client'); const customClient = httpClient.create({ requestPhases: 'early', 'initial', 'middle', 'final', responsePhases: 'initial', 'middle', 'final', 'end', });
Example (Use default instance)
Kind: inner method of oniyi-http-client
Returns: HttpClient - The newly created HttpClient instance
| Param | Type | Default | Description | | --- | --- | --- | --- | | options | Object |
Example (with request defaults)
Returns: Object - a clone of this instance's defaults object
Kind: instance method of HttpClient
Returns: Object - CookieJar
| Param | Type | Description | | --- | --- | --- | | store | Object | tough-cookie Store |
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 |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Properties
| Name | Type | Description | | --- | --- | --- | | phaseName | string | Name of the phase that
| Param | Type | Description | | --- | --- | --- | | context | Object | An object with the currently available request context. Hooks in the
Kind: global typedef
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | hookState | Hookstate | | | options | Object | request options |
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) |
Kind: global typedef
Kind: global typedef
Kind: global typedef
Kind: global typedef
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 instanceconst 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:
- credentials
- async cookie jars
- caching
- throttling
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 phasesinitial
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 ```js 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](#PluginHookHandler).
The received context argument is an [OnRequestContext](#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](#PluginHookHandler).
The received context argument is an [OnResponseContext](#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.
```js
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 configurationExample (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 scenariosKind: 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* [.#defaults()](#markdown-header-httpclientdefaults-object) ⇒ Object
* [.#jar([store])](#markdown-header-httpclientjarstore-object) ⇒ Object
* [.#use(plugin, [options])](#markdown-header-httpclientuseplugin-options-object) ⇒ Object
* [.#makeRequest(uri, [options], [callback])](#markdown-header-httpclientmakerequesturi-options-callback-requestpromise) ⇒ RequestPromise
* [.#get(uri, [options], [callback])](#markdown-header-httpclientgeturi-options-callback-requestpromise) ⇒ RequestPromise
* [.#put(uri, [options], [callback])](#markdown-header-httpclientputuri-options-callback-requestpromise) ⇒ RequestPromise
* [.#post(uri, [options], [callback])](#markdown-header-httpclientposturi-options-callback-requestpromise) ⇒ RequestPromise
* [.#del(uri, [options], [callback])](#markdown-header-httpclientdeluri-options-callback-requestpromise) ⇒ RequestPromise
* [.#head(uri, [options], [callback])](#markdown-header-httpclientheaduri-options-callback-requestpromise) ⇒ RequestPromise
* [.#options(uri, [options], [callback])](#markdown-header-httpclientoptionsuri-options-callback-requestpromise) ⇒ RequestPromise
httpClient.#defaults() ⇒ Object
Kind: instance method of HttpClientReturns: Object - a clone of this instance's defaults object
httpClient.#jar(store) ⇒ Object
Create a new CookieJar with the provided Store implementation. Will userequest.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 HttpClientReturns: 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 oneoptions
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 forcesoptions.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 forcesoptions.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 forcesoptions.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 forcesoptions.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 forcesoptions.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 forcesoptions.method
to OPTIONS
Kind: instance method of HttpClient
| Param | Type | | --- | --- | | uri | RequestArgUri | | options | RequestArgOptions | | callback | RequestArgCallback |
Type Definitions
PluginHook : Object
Kind: global typedefProperties
| 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 theonRequest
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 theonRequest
phaseListKind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | hookState | Hookstate | | | options | Object | request options |
OnResponseContext : Object
mutable context object that gets passed through all phases in theonResponse
phaseListKind: 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 aurl
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 eitheroptions
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 OnResponseContextKind: global typedef
RequestPromise : Promise
A Promise that resolves to the return value forrequest()
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