Oniyi Http Client
Adding a plugin interface to request that allows modifications of request parameters and response data
Installation
npm install --save oniyi-http-client
Usage
Use default instance
const httpClient = ;httpClient;
with request defaults
const httpClient = ;const customClient = httpClient; customClient;
with custom phases
const httpClient = ;const customClient = httpClient;
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 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 = ;const customClient = httpClient;
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' { const options hookState = ctx; // store something in the state shared across all hooks for this request _; ; } onResponse: phaseName: 'final' { const hookState = ctx; // read value from state again const name = _; ; } ; client ;
API
HttpClient
oniyi-http-client : The default HttpClient instance. Can be used without any further configuration
Example (Use default instance)
const httpClient = ;httpClient;
HttpClient
oniyi-http-client~create([options]) ⇒ 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.<String> |
complete list of phase names for the onRequest phaseList. must include the names initial and final |
|
[options.responsePhases] | Array.<String> |
complete list of phase names for the onResponse phaseList. must include the names initial and final |
Example (with request defaults)
const httpClient = ;const customClient = httpClient; customClient;
Example (with custom phases)
const httpClient = ;const customClient = httpClient;
HttpClient
Kind: global class
- HttpClient
- .#defaults() ⇒
Object
- .#jar([store]) ⇒
Object
- .#use(plugin, [options]) ⇒
HttpClient
- .#makeRequest(uri, [options], [callback]) ⇒
RequestPromise
- .#get(uri, [options], [callback]) ⇒
RequestPromise
- .#put(uri, [options], [callback]) ⇒
RequestPromise
- .#post(uri, [options], [callback]) ⇒
RequestPromise
- .#del(uri, [options], [callback]) ⇒
RequestPromise
- .#head(uri, [options], [callback]) ⇒
RequestPromise
- .#options(uri, [options], [callback]) ⇒
RequestPromise
- .#defaults() ⇒
Object
httpClient.#defaults() ⇒ Kind: instance method of HttpClient
Returns: Object
- a clone of this instance's defaults object
Object
httpClient.#jar([store]) ⇒ 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
httpClient.#use(plugin, [options]) ⇒ Kind: instance method of HttpClient
Returns: HttpClient
- this HttpClient instance
Param | Type |
---|---|
plugin | Object |
plugin.name | String |
[plugin.onRequest] | Array.<PluginHook> |
[plugin.onResponse] | Array.<PluginHook> |
[options] | Object |
RequestPromise
httpClient.#makeRequest(uri, [options], [callback]) ⇒ 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 |
RequestPromise
httpClient.#get(uri, [options], [callback]) ⇒ Same as #makeRequest but forces options.method
to GET
Kind: instance method of HttpClient
Param | Type |
---|---|
uri | RequestArgUri |
[options] | RequestArgOptions |
[callback] | RequestArgCallback |
RequestPromise
httpClient.#put(uri, [options], [callback]) ⇒ Same as #makeRequest but forces options.method
to PUT
Kind: instance method of HttpClient
Param | Type |
---|---|
uri | RequestArgUri |
[options] | RequestArgOptions |
[callback] | RequestArgCallback |
RequestPromise
httpClient.#post(uri, [options], [callback]) ⇒ Same as #makeRequest but forces options.method
to POST
Kind: instance method of HttpClient
Param | Type |
---|---|
uri | RequestArgUri |
[options] | RequestArgOptions |
[callback] | RequestArgCallback |
RequestPromise
httpClient.#del(uri, [options], [callback]) ⇒ Same as #makeRequest but forces options.method
to DELETE
Kind: instance method of HttpClient
Param | Type |
---|---|
uri | RequestArgUri |
[options] | RequestArgOptions |
[callback] | RequestArgCallback |
RequestPromise
httpClient.#head(uri, [options], [callback]) ⇒ Same as #makeRequest but forces options.method
to HEAD
Kind: instance method of HttpClient
Param | Type |
---|---|
uri | RequestArgUri |
[options] | RequestArgOptions |
[callback] | RequestArgCallback |
RequestPromise
httpClient.#options(uri, [options], [callback]) ⇒ Same as #makeRequest but forces options.method
to OPTIONS
Kind: instance method of HttpClient
Param | Type |
---|---|
uri | RequestArgUri |
[options] | RequestArgOptions |
[callback] | RequestArgCallback |
Type Definitions
Object
PluginHook : 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 |
function
PluginHookHandler : 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 |
Object
Hookstate : 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
Object
OnRequestContext : 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 |
Object
OnResponseContext : 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) |
String
| Object
RequestArgUri : 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
Object
| function
RequestArgOptions : The sesond argument can bei either
options
object or callback
function.
Kind: global typedef
function
RequestArgCallback : 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
Promise
RequestPromise : 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