Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.1.1] - 2026-08-17
Changed
- Codec modules now live under the
Dowser.Client.Codecnamespace, for consistency withDowser.Client.HTTP.*andDowser.Client.JSON.*:Dowser.Client.CodecBuilderis nowDowser.Client.Codec.Builder, andDowser.Client.Codecs.DefaultCodec— the default:codec_adapter— is nowDowser.Client.Codec.Default. Behavior is unchanged.
Deprecated
Dowser.Client.CodecBuilder— kept as an alias delegating toDowser.Client.Codec.Builder;use Dowser.Client.CodecBuilderstill works but now emits a deprecation warning at compile time. To be removed in a future release.Dowser.Client.Codecs.DefaultCodec— kept as an alias delegating toDowser.Client.Codec.Default, so a config or request that names it explicitly as:codec_adapterkeeps working. To be removed in a future release.
Fixed
- The docs no longer reference the private
Dowser.CoreExt.Keyableprotocol, which mademix docswarn about documentation referencing a hidden function from the README,Dowser.Client.CodecandDowser.Client.Codec.Default.
[0.1.0] - 2026-08-17
Initial release.
Added
Dowser.Client— low-level entry point for querying a search backend over HTTP (request/4, plusget/post/put/patch/deleteshortcuts). Resolves a config and request options, performs the HTTP call, retries transient failures, and decodes the response body.Dowser.Client.Config— bundles an:endpoint,:auth, and the HTTP/JSON/ codec adapters and options to use for one search backend. Configs can be built inline withnew/1or configured by name at compile time (config :dowser_client, configs: [...]) and resolved by atom, map/keyword list, or omitted entirely (resolves the:defaultnamed entry — there is no other built-in fallback).inspect/1redacts the:authsecret.- Pluggable HTTP transport via
Dowser.Client.HTTP.Adapter, with three bundled implementations, all pinned to HTTP/1.1 with keep-alive:Dowser.Client.HTTP.Httpc— Erlang's built-in:httpc(no extra dependency), used by default.Dowser.Client.HTTP.Req— theReqlibrary (optional dependency).Dowser.Client.HTTP.Hackney—:hackney(optional dependency).- Sensible default connect (2s) and receive (30s) timeouts on every
adapter, always overridable per request via
:http_opts. - Adapter selection honors runtime application config
(
config :dowser_client, :http_adapter | :json_adapter | :codec_adapter), not just compile-time settings.
- Automatic retries on transient failures — connection errors (refused,
closed, timed out, unreachable) and retryable HTTP statuses (
429,502,503,504) — with exponential backoff and full jitter. On by default for every request/method; configurable or disable-able via:retry. - Pluggable JSON codec via
Dowser.Client.JSON.Adapter, with three bundled implementations:Dowser.Client.JSON.Native— Elixir's built-inJSONmodule (no extra dependency), used by default.Dowser.Client.JSON.Jason— theJasonlibrary (optional dependency).Dowser.Client.JSON.Poison— thePoisonlibrary (optional dependency).
- Request/response body formats:
:json(default),:ndjsonfor bulk-style newline-delimited payloads, and:rawto pass bytes through untouched;:req_format/:resp_formatset each direction independently. Dowser.Client.Field— a behaviour for casting a single value to and from its wire representation (load/2/dump/2), for the backend-specific knowledgedowser_clientdoesn't have; backend packages likedowser_elasticsearchship their own field implementations.Dowser.Client.CodecBuilder—use-able macro that builds aload/2/dump/2dispatcher from a list ofDowser.Client.Fieldmappings declared withcast/2, pattern-matched against field metadata. Expands to plain pattern-matched function clauses at compile time, with:inherit,:fallbackand:niloptions.Dowser.Client.Codec— a behaviour for casting a whole request/response body (encode/2/decode/2), wired onto a config/request as:codec_adapter.Dowser.Client.Codecs.DefaultCodecis the built-in default, applying only:keyscasting; a backend package composes its ownCodecon top of aCodecBuilder-built dispatcher for per-field value casting (dates, geo points, ...), and:codec_opts(settable as aDowser.Client.Configdefault and per-request overridable) is forwarded to it.:keys(:stringsdefault,:atoms,:atoms!) to cast a decoded body's keys — however deeply nested — resolved into a:key_fnpassed to:codec_adapter; settable as aDowser.Client.Configdefault and per-request overridable.- Authentication helpers on
Dowser.Client.Configfor:basic,:bearer,:api_key, and arbitrary:headerauth, applied as request headers. - Header merging across global (
config :dowser_client, :http_opts, headers: ...), config, and per-request sources, with later (more specific) sources winning on name clashes. - Normalized error types so callers never see a dependency's own exception:
Dowser.Client.Error(config resolution, invalid:format/:keys/:retry, missing optional adapter dependency),Dowser.Client.JSON.Error(encode/decode failures),Dowser.Client.Codec.Error(:codec_adapterencode/2/decode/2failures), andDowser.Client.HTTP.Error(transport failures, reported with the number of attempts made). Dowser.unwrap/1to unwrap a{:ok, value} | {:error, exception}result into the plain value or a raised exception.Dowser.Client.HTTP.Stub— a fourthHTTP.Adapterfor tests.stub/1scripts the response for every request made by the calling process (no shared/global state, safe underasync: true), andjson/3/raw/3build matching responses, so a test can drive the full request/response pipeline without a real backend running.
Known limitations
Dowser.Client.HTTP.Httpccannot send a request body onGET/HEAD/TRACE(Erlang's:httpchas no body slot for those methods) — it returns{:error, {:unsupported_body, method}}rather than silently dropping the body. Backends that expect a body onGET(e.g. Elasticsearch'sGET /_search) needDowser.Client.HTTP.ReqorDowser.Client.HTTP.Hackney.