An HTTP(S) client designed for REST API
hapi
is a library that provides functions to request REST API backends. It does many
things under the hood, like DNS resolving, request retrying, JSON validation and so on.
The API functions are exported from hapi and hapi_json modules.
Other modules (like hapi_xml
) can be introduced later.
For hapi_json module the library is using jiffy codec to decode and yval validator to validate received JSON.
The following types are exported from hapi
module:
-
uri()
: an URI represented in format produced byuri_string:uri_map()
. -
req_opts()
: a map holding request options. All options are optional. Available options are:-
timeout: non_neg_integer() | {abs, non_neg_integer()}
: A non-negative integer representing either timeout in milliseconds or an absoulute time in milliseconds which denotes when the function must be completed. It is important to stress that for{abs, Time}
theTime
is computed usingerlang:system_time(millisecond)
, but NOTerlang:monotonic_time(millisecond)
. If you assume the later, you get an incorrect behaviour. Also note that several HTTP requests can be performed during a single function call, so don't confuse this option withtimeout_per_request
(see bellow). The default value is30000
. -
timeout_per_request: non_neg_integer()
: A non-negative integer representing timeout in milliseconds that is used for a particual HTTP request. Since many HTTP requests may be performed during a single function call (due to multiple resolved IP addresses or several attempts being repeated), the value of the option must be selected the way that it fits into general timeout represented bytimeout
option (see above). The default is the value oftimeout
option divided by the number of resolved IP addresses, e.g. iftimeout
is5000
milliseconds and three addresses have been resolved, a request to each of them will be completed within1666
milliseconds. -
max_retries: non_neg_integer() | infinity
. A maximum number of retries to be performed. The default isinfinity
, i.e. the request is to be repeated until it is succeeded or timeout is reached. In particular, the value of zero (0) means no retries will be performed. -
retry_base_timeout: non_neg_integer()
: A time (in milliseconds) to wait before next try after first failed attempt. The value is exponentially increased for further retries (if any). The default value is1000
milliseconds. -
auth
: a map representing HTTP authorization options. All options are mandatory. The options are:type: atom()
: authorization type. Currently onlybasic
is supported.username: iodata()
: a user name to use for the authorization.password: iodata()
: a password to use for the authorization.
-
headers: [{binary(), binary()}]
: Additional HTTP headers. The default is an empty list ([]
). -
ip_family: [inet | inet6, ...]
: An IP address version to resolve:inet
stands for IPv4 andinet6
stands for IPv6. The default is[inet]
which means that only IPv4 addresses will be resolved. -
use_pool: boolean()
: Whether to use connection pool or not. The default isfalse
. The pool itself is configured using application environment variablespool_size
andmax_queue
with default values being10
and10000
respectively. Thepool_size
sets the maximum number of connections per endpoint (i.e. per IP-address/port pair). The pool is dynamic in the sense that it keeps only required number of connections, i.e. new connections are added to the pool only when all other connections are request-busy. When the number of connections in the pool reachespool_size
number, no new connections are added to the pool. The variablemax_queue
defines the maximum number of requests in the pool request queue. When the queue is overfilled (i.e.max_queue
is reached) the pool is first cleaned up from overdue requests, then, if the request queue is still filled with more than 80% of its capacity, the pool is completely cleared with all its pending requests being discarded with the corresponding error. -
trace: false | {domain, atom()}
: Flag to enable debug logging of request-response pair. Disabled by default. If set, will emit log messages to specified domain.
-
-
method() :: get | post | delete
: an HTTP method. -
headers()
: HTTP headers represented as[{binary(), binary()}]
. -
http_reply() :: {Status :: non_neg_integer(), Headers :: headers(), Body :: binary()}
: A successful HTTP reply. -
error_reason()
: aterm()
representing an error reason.
-spec get(URI :: uri()) -> {ok, http_reply()} | {error, error_reason()}.
-spec get(uri(), req_opts()) -> {ok, http_reply()} | {error, error_reason()}.
Performs an HTTP GET request.
-spec post(URI :: uri(), Body :: iodata()) -> {ok, http_reply()} | {error, error_reason()}.
-spec post(uri(), iodata(), req_opts()) -> {ok, http_reply()} | {error, error_reason()}.
Performs an HTTP POST request.
-spec delete(uri()) -> {ok, http_reply()} | {error, error_reason()}.
-spec delete(uri(), req_opts()) -> {ok, http_reply()} | {error, error_reason()}.
Performs an HTTP DELETE request.
-spec format_error(Reason :: error_reason()) -> string().
Returns a descriptive string of the error reason in English.
-spec proxy_status(http_reply() | error_reason()) -> non_neg_integer().
Given an HTTP reply or an error reason, produces the corresponding HTTP status code. Useful for proxying responses downstream.
The following types are exported from hapi_json
module:
-
problem_report()
: A map representing problem details as described in RFC7807 - the module supports the RFC and is able to understandapplication/problem+json
content type. The map has the following structure:#{status := non_neg_integer(), type => binary(), title => binary(), detail => binary(), _ => term()}.
All keys represent JSON fields explained in the RFC.
-
json_error_reason()
: aterm()
representing the reason of JSON decoding/validation failure. -
error_reason()
: aterm()
representing an error reason. Not to be confused withhapi:error_reason()
and hence, NOT to be used inhapi:format_error/1
.
-spec get(URI :: hapi:uri(),
Validator :: yval:validator(T)) -> {ok, T | no_content} | {error, error_reason()}.
-spec get(URI :: hapi:uri(),
Validator :: yval:validator(T),
Options :: hapi:req_opts()) -> {ok, T | no_content} | {error, error_reason()}.
Performs an HTTP GET request and decodes JSON response body.
The Validator
is used to validate decoded JSON.
-spec post(URI :: hapi:uri(),
JSON :: jiffy:json_value(),
Validator :: yval:validator(T)) -> {ok, T | no_content} | {error, error_reason()}.
-spec post(URI :: hapi:uri(),
JSON :: jiffy:json_value(),
Validator :: yval:validator(T),
Options :: hapi:req_opts()) -> {ok, T | no_content} | {error, error_reason()}.
Performs an HTTP POST request and decodes JSON response body.
The Validator
is used to validate decoded JSON.
-spec delete(URI :: hapi:uri(),
Validator :: yval:validator(T)) -> {ok, T | no_content} | {error, error_reason()}.
-spec delete(URI :: hapi:uri(),
Validator :: yval:validator(T),
Options :: hapi:req_opts()) -> {ok, T | no_content} | {error, error_reason()}.
Performs an HTTP DELETE request and decodes JSON response body.
The Validator
is used to validate decoded JSON.
-spec decode(Data :: binary(),
Validator :: yval:validator(T)) -> {ok, T | no_content} | {error, json_error_reason()}.
Decodes and validates JSON represented as binary()
.
-spec encode(jiffy:json_value()) -> iodata().
Encodes JSON.
-spec format_error(error_reason()) -> string().
Returns a descriptive string of the error reason in English. You can also format error reasons produced by hapi module, but NOT other way around.
-spec proxy_status(error_reason()) -> non_neg_integer().
Given an HTTP reply or an error reason, produces the corresponding HTTP status code. Useful for proxying responses downstream.