A status code driven JSON HAL HTTP client based on the fetch API.
Module Members
Types
Virtual status code 'norel'
for a missing relation to use as key in the on
-handlers map.
Creates a new http client for usage with a RESTful backend supporting the content type
application/hal+json
(https://tools.ietf.org/html/draft-kelly-json-hal-06).
Example:
const hal = create( {
on: {
'xxx'( data, response ) {
console.log( 'I\'ll handle everything not handled locally' );
}
}
} );
hal.get( 'http://host/someResource' )
.on( {
'2xx'( data, response ) {
console.log( 'Everything looks fine: ', data );
return hal.follow( data, 'some-relation' );
},
'4xx|5xx'( data, response ) {
console.log( 'Server or client failed. Who knows? The status!', response.status );
}
} )
// handle the response from following 'some-relation'
.on( {
'200'( data, response ) {
console.log( 'I got this: ', data );
},
'norel'() {
console.log( 'Oh no, seems "some-relation" is missing in the representation' );
}
} );
See #ResponsePromise
for further information on the on
function.
Property | Type | Description |
---|---|---|
optionalOptions | Object |
map of global configuration to use for the HAL client |
optionalOptions.queueUnsafeRequests | Boolean |
if true an unsafe request (DELETE, PATCH, POST and PUT) has to be finished before the next is started. Default is false |
optionalOptions.headers | Object |
global headers to send along with every request |
optionalOptions.middlewares | Array.<{ request: Function, response: Function }> |
optional array of middlewares to preprocess requests and to postprocess responses. Each middleware is an object with a request and a response method. The request method is called everytime that a request is made. It is invoked with a single argument of the form { url: String, init: Object } . The url is the target URL and init contains the fetch-init options as they would be passed to fetch if no middleware was present. The method must return an object of the same shape, or a Promise for such an object. The response method is called everytime a response is received. It is invoked with single argument of the form { response: Response, url: String, init: Object } . The response is the response from the server, and url and init are the URL and init options that were passed to fetch to obtain that response. The method must return an object of the same shape, or a Promise for such an object. When multiple middlewares are used, they are run left-to-right (requests) and right-to-left (responses). The result (of the promise) generated by each middleware is passed to the next middleware. The result of the rightmost middleware's request method is passed to the fetch-API of the browser. The result of the leftmost middleware's response method is used for further response processing (e.g. calling on-handlers). Note that you can freely transform requests and responses, or even make intermediate requests. In request middleware, you could e.g. completely rewrite the URL. In response middleware, you are not obliged to return the same response: you could make a different fetch-request instead and return the response of that request. |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used with every request. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
optionalOptions.on | Object |
global on handlers to use as fallback if no matching handler was found in an on call |
optionalOptions.responseTransformer | Function |
a function that is called for every response and must return an optionally transformed version of that response. This can e.g. be used for URL rewriting of proxied requests during development. This should not be used in production for transformation of actual data |
optionalOptions.logError | Function |
a function to log error messages to. By default console.error is used |
optionalOptions.logDebug | Function |
a function to log debug / development messages to. By default console.debug is used |
Type | Description |
---|---|
HalHttpClient |
a new HAL client instance |
Returns a copy of the given HAL representation with all HAL media type specific properties removed.
Currently these are _links
and _embedded
.
Property | Type | Description |
---|---|---|
halRepresentation | Object |
the representation to clean up |
Type | Description |
---|---|
Object |
the copy without HAL media type keys |
Returns true
if the given relation exists as link or is embedded.
Property | Type | Description |
---|---|---|
halRepresentation | Object |
HAL representation to check for the relation |
relation | String |
name of the relation to find |
Type | Description |
---|---|
Boolean |
true if relation exists in the representation |
Returns the first value of href for the requested relation. Search for the relation starts under
_links
and continues in _embedded
, if not found in _links
. If not found at all, null
is
returned.
If the relation is found and yields only a single value, that value's href
attribute value is
returned. If the relation yields a list, the href
attribute value of the first entry is returned.
Property | Type | Description |
---|---|---|
halRepresentation | Object |
the representation to search for the relation |
relation | String |
the relation to get a href attribute value from |
Type | Description |
---|---|
String |
the href attribute value if available, null otherwise |
Returns the first value of href for the self relation. The same as for #firstRelationHref
holds,
but normally a self relation should always be present for a RESTful webservice.
Property | Type | Description |
---|---|---|
halRepresentation | Object |
the representation to search for the self relation |
Type | Description |
---|---|
String |
the href attribute value if available, null otherwise |
Makes a GET request for the given URL or HAL representation. In case a HAL representation is given,
the self
relation in the _links
map is used to derive the URL for the request.
Property | Type | Description |
---|---|---|
urlOrHalRepresentation | String , Object |
a URL or a HAL representation to make the request for |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.headers | Object |
headers to send along with the request. By default, Accept: application/hal+json, application/json;q=0.8 is added to the headers |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Makes a HEAD request for the given URL or HAL representation.
In case a HAL representation is given, the self
relation in the _links
map is used to derive the URL
for the request.
Property | Type | Description |
---|---|---|
urlOrHalRepresentation | String , Object |
an URL or a HAL representation to make the request for |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.headers | Object |
headers to send along with the request. By default no headers are set |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Makes a PUT request for the given URL or HAL representation. In case a HAL representation is given,
the self
relation in the _links
map is used to derive the URL for the request.
Property | Type | Description |
---|---|---|
urlOrHalRepresentation | String , Object |
an URL or a HAL representation to make the request for |
body | Object |
JSON serializable body to send |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.headers | Object |
headers to send along with the request. By default Accept: application/hal+json and Content-Type: application/json are added to the headers |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Makes a POST request for the given URL or HAL representation. In case a HAL representation is given,
the self
relation in the _links
map is used to derive the URL for the request.
Property | Type | Description |
---|---|---|
urlOrHalRepresentation | String , Object |
an URL or a HAL representation to make the request for |
body | Object |
JSON serializable body to send |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.headers | Object |
headers to send along with the request. By default, Accept: application/hal+json, application/json;q=0.8 and Content-Type: application/json are added to the headers |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Makes a PATCH request for the given URL or HAL representation. In case a HAL representation is given,
the self
relation in the _links
map is used to derive the URL for the request.
Property | Type | Description |
---|---|---|
urlOrHalRepresentation | String , Object |
a URL or a HAL representation to make the request for |
body | Object |
body in JSON Patch notation (http://tools.ietf.org/html/rfc6902) |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.headers | Object |
headers to send along with the request. By default, Accept: application/hal+json, application/json;q=0.8 and Content-Type: application/json-patch+json are added to the headers |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Makes a DELETE request for the given URL or HAL representation. In case a HAL representation is given,
the self
relation in the _links
map is used to derive the URL for the request.
Property | Type | Description |
---|---|---|
urlOrHalRepresentation | String , Object |
an URL or a HAL representation to make the request for |
body | Object |
JSON serializable body to send. If you want to use options, but have no body , use undefined as value for body |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.headers | Object |
headers to send along with the request. By default Accept: application/hal+json, application/json;q=0.8 and Content-Type: application/json are added to the headers |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Follows one or more resources of a relation within a given HAL representation. First it is checked if
a representation for the relation is already embedded and in case it exists, this will be the result.
If that isn't the case, the _links
property is searched for a URL of that relation and if found, a
GET request for this URL is performed. If the relation could not be found in the given representation
the resulting promise is rejected.
If there are multiple links or embedded resources, by default only the first one will be requested and
its response passed to the consumers of the promise. In case the followAll
option is set to true
,
all found embedded representations are returned or all relations found in the _links
property are
requested resp.. The resulting promise will then be resolved with an array of responses instead of a
single response. As there might be different status codes for the responses, a specific on
handler is
only called if all status codes yield the same value. In any other case only the handler for xxx
is
called. This can be prevented, if a list resource always embeds the representations of its items.
Property | Type | Description |
---|---|---|
halRepresentation | Object |
the representation whose relation should be followed |
relation | String |
the relation to follow |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.method | Object |
method to use for the request(s). If not GET , embedded representations will be ignored. Default is GET |
optionalOptions.body | Object |
JSON serializable body to send |
optionalOptions.headers | Object |
headers to send along with the request. The same default headers as for get() are used |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
optionalOptions.followAll | Boolean |
if true , follows all entities found for that relation. Default is false |
optionalOptions.vars | Object |
map of variables to replace in templated URLs |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
A shortcut function for #HalHttpClient.follow()
called with followAll
yielding true
:
follow( halRepresentation, relation, { followAll: true } )
.
Property | Type | Description |
---|---|---|
halRepresentation | Object |
the representation whose relation should be followed |
relation | String |
the relation to follow |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.method | Object |
method to use for the request(s). If not GET , embedded representations will be ignored. Default is GET |
optionalOptions.body | Object |
JSON serializable body to send |
optionalOptions.headers | Object |
headers to send along with the request. The same default headers as for get() are used |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
optionalOptions.vars | Object |
map of variables to replace in templated URLs |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the response |
Helper factory for follow()
function calls. The returned function only expects a HAL representation as
argument, and calls #HalHttpClient.follow()
using that representation as first argument.
The purpose of this method is the use within chained follow()
calls, especially in on
handlers.
Example:
halClient.get( 'http://host/office' )
.on( { '200': halClient.thenFollow( 'desk' ) } )
.on( { '200': halClient.thenFollow( 'computer' ) } )
.on( { '200': halClient.thenFollow( 'keyboard' ) } );
// ...
Assuming every response yields a status of 200
, first a representation of an office resource is
fetched, then the desk
relation is followed, then within the resulting representation the computer
relation is followed and finally within that representation the keyboard
relation is followed.
Note that this method cannot be used in an on
handler after a followAll
request, as there will be
an array of objects instead of only one object.
Property | Type | Description |
---|---|---|
relation | String |
the relation to follow |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.method | Object |
method to use for the request(s). If not GET , embedded representations will be ignored. Default is GET |
optionalOptions.body | Object |
JSON serializable body to send |
optionalOptions.headers | Object |
headers to send along with the request. The same default headers as for get() are used |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
optionalOptions.followAll | Boolean |
if true , follows all entities found for that relation. Default is false |
Type | Description |
---|---|
Function |
a function calling follow on the response it receives |
A shortcut function for #HalHttpClient.thenFollow()
called with followAll
yielding true
:
thenFollow( relation, { followAll: true } )
.
Property | Type | Description |
---|---|---|
relation | String |
the relation to follow |
optionalOptions | Object |
configuration to use for the request |
optionalOptions.method | Object |
method to use for the request(s). If not GET , embedded representations will be ignored. Default is GET |
optionalOptions.body | Object |
JSON serializable body to send |
optionalOptions.headers | Object |
headers to send along with the request. The same default headers as for get() are used |
optionalOptions.fetchInit | Object |
additional init options for fetch to be used for this request only. The keys headers , body and method are ignored from this option, since they are either parameters on their own or implemented as specific function. |
Type | Description |
---|---|
Function |
a function calling followAll on the response it receives |
extends
Promise
A simple extension of a normal Promise. Its purpose is to add some convenience when following relations of a resource. Using the standard Promise API is still possible.
A function to register handlers for the possible HTTP status codes returned by the API. This is the actual heart of this library.
This function has to be called with a map of status codes to functions responsible for handling the response that was given for an actual status code. It is possible to group status codes using the same handler for their codes. And lastly wildcards are possible to be able to treat a specific class of status codes conveniently the same way.
Let's have a look at an example:
const handler1 = ( result, response ) => {};
const handler2 = ( result, response ) => {};
const handler3 = ( result, response ) => {};
const handler4 = ( result, response ) => {};
hal.get( 'my-resource' )
.on( {
'200': handler1,
'201|202|204': handler2,
'5xx': handler3
} );
Here handler1
will only be called for status code 200, handler2
for the given status codes
201, 202 and 204, and handler3
will be called for any type of server error. A final catch all
handler could have also been added simply using a full wildcard string xxx. Any code that is not
handled by this map of handlers is forwarded to the global handlers map (see create()
). In
case there is no handler there either, this will be logged and the next returned promise will be
rejected.
Each handler receives to arguments: First, the body of the response (already parsed from a JSON
string into a JavaScript object). The second argument is the plain response object as returned by
the underlying fetch
API. In case the entries of a list resource were fetched the arguments will
be arrays, carrying the body and response objects of all list items.
If the response cannot be parsed into valid JSON (for example, if the server returns an HTML error
page which may often happen in case of a 4xx
or 5xx
), the status code will be kept, but the
result
object is set to null
. In this case, interested handlers can still inspect the complete
response for details.
Handlers can then further follow relations of the provided body object by using the convenience
methods #HalHttpClient.follow()
or #HalHttpClient.followAll()
, and returning the
resulting ResponsePromise
for typical Promise-like chaining. If a handler really does nothing apart
from following a relation of the HAL response, a generic handler can even be created by using
#HalHttpClient.thenFollow()
or #HalHttpClient.thenFollowAll()
. In addition to the
http status codes and xxx a "virtual" code of 'norel'
can be used to handle the case, where a
relation is missing in a response.
If a handler returns nothing or null
, and by that indicating an empty response, subsequent handlers
will never be called.
Special cases
- An empty list resource: This will be returned with overall status code 200.
- Different status codes for the list items: This will only trigger the xxx handler.
- The relation to follow doesn't exist: The norel handler will be called
Property | Type | Description |
---|---|---|
handlers | Object |
the map of handlers as described above |
Type | Description |
---|---|
ResponsePromise |
an extended promise for the result of the handler that was called |