# `Supabase.Fetcher.Request`

Composable HTTP request builder for Supabase services.

This module is primarily used internally by downstream libraries (`supabase_auth`,
`supabase_storage`, `supabase_realtime`, etc.) to construct requests. You can also
use it directly for full granular control over requests.

Provides a builder pattern to construct requests with method, headers, body,
query params, and service-specific URLs (`:auth`, `:storage`, `:functions`, etc.).

## HTTP Client

The underlying HTTP adapter is resolved from application config:

    config :supabase_potion, http_client: MyApp.CustomHTTPClient

Defaults to `Supabase.Fetcher.Adapter.Finch`. Any module implementing the
`Supabase.Fetcher.Adapter` behaviour can be used.

## Example

    {:ok, response} =
      Supabase.Fetcher.new(client)
      |> Supabase.Fetcher.with_auth_url("/token")
      |> Supabase.Fetcher.with_method(:post)
      |> Supabase.Fetcher.with_body(%{username: "test", password: "test"})
      |> Supabase.Fetcher.request()

# `options`

```elixir
@type options() :: [decode_body?: boolean(), parse_http_error?: boolean()]
```

# `t`

```elixir
@type t() :: %Supabase.Fetcher.Request{
  body: Supabase.Fetcher.body(),
  body_decoder: module(),
  body_decoder_opts: keyword(),
  client: Supabase.Client.t(),
  error_parser: module(),
  headers: Supabase.Fetcher.headers(),
  http_client: module(),
  http_client_opts: keyword(),
  method: Supabase.Fetcher.method(),
  options: options(),
  query: Supabase.Fetcher.query(),
  service: Supabase.service(),
  url: Supabase.Fetcher.url()
}
```

# `get_header`

```elixir
@spec get_header(t(), name :: String.t(), default :: String.t() | nil) ::
  String.t() | nil
```

Tries to find and return the value for a request headers, given it name, if it doesn't
existis, it returns the default value informed or `nil`.

Do not confuse with `Supabase.Response.get_header/2`.

# `get_query_param`

```elixir
@spec get_query_param(t(), param :: String.t(), default :: String.t() | nil) ::
  String.t() | nil
```

Tries to find and return the value for a query param, given it name, if it doesn't
existis, it returns the default value informed or `nil`.

# `merge_query_param`

```elixir
@spec merge_query_param(t(), param :: String.t(), value :: String.t(), [
  {:with, joinner :: String.t()}
]) ::
  t()
```

Merges an existing query param value with a new one, prepending the new value
with the existing one. If no current value exists for the param, this function
will behave the same as `with_query/2`.

# `merge_req_header`

```elixir
@spec merge_req_header(t(), header :: String.t(), value :: String.t(), [
  {:with, joinner :: String.t()}
]) ::
  t()
```

Merges an existing request header value with a new one, prepending the new value
with the existing one. If no current value exists for the header, this function
will behave the same as `with_headers/2`.

# `new`

Initialise the `Supabase.Fetcher` struct, accumulating the
client global headers and the client itself, so the request can be
easily composed using the `with_` functions of this module.

# `with_auth_url`

Applies the auth base url from the client and appends the
informed path to the url. Note that this function will overwrite
the `url` field each time of call.

# `with_body`

Defines the request body to be sent, it can be a map, that will be encoded
with `encode_to_iodata!/1`, any `iodata` or a stream body in the pattern of `{:stream, Enumerable.t}`, although you will problably prefer to use the `upload/2`
function of this module to hadle body stream since it will handle file management, content headers and so on.

Additionally, multipart/form-data bodies are supported via:
- `{:multipart, parts}` - In-memory multipart encoding
- `{:multipart_stream, parts}` - Streaming multipart encoding for large files

# `with_body_decoder`

Attaches a custom body decoder to be called after a successfull response.
The body decoder should implement the `Supabase.Fetcher.BodyDecoder` behaviour, and it default
to the `Supabase.Fetcher.JSONDecoder`, or it can be a 2-arity function that will follow the `Supabase.Fetcher.BodyDecoder.decode` callback interface.

You can pass `nil` as the decoder to avoid body decoding, if you need the raw body.

# `with_database_url`

Applies the database base url from the client and appends the
informed path to the url. Note that this function will overwrite
the `url` field each time of call.

# `with_error_parser`

Attaches a custom error parser to be called after a "successfull" response.
The error parser should implement the `Supabase.Error` behaviour, and it default
to the `Supabase.HTTPErrorParser`.

"successful" response means that the fetcher actually got a HTTP response from
the server, so if the HTTP status is >= 400, so this error parser will be invoked.

Check `Supabase.HTTPErrorParser` for an example of implementation.

# `with_functions_url`

Applies the functions base url from the client and appends the
informed path to the url. Note that this function will overwrite
the `url` field each time of call.

# `with_headers`

Append headers to the current request builder, the headers needs to be an
`Enumerable.t()` and will be merged via `merge_headers/2`, which means that
this function can be called multiple times **without** overwriting the existing
headers definitions.

# `with_http_client`

Registers a custom HTTP client backend to be used by `Supabase.Fetcher` while
dispatching the request. The default one is `Supabase.Fetcher.Adapter.Finch`

# `with_method`

Define the method of the request, default to `:get`, the available options
are the same of `Finch.Request.method()` and note that this function
will overwrite the `method` attribute each time is called.

# `with_query`

Append query params to the current request builder, it receives an `Enumerable.t()`
and accumulates it into the current request. This function behaves the same as
`with_headers/2`, so it is **rigt-associative**, meaning that duplicate keys
informed will overwrite the last value.

Finally, before the request is sent, the query will be encoded with `URI.encode_query/1`

# `with_realtime_url`

Applies the realtime base url from the client and appends the
informed path to the url. Note that this function will overwrite
the `url` field each time of call.

# `with_storage_url`

Applies the storage base url from the client and appends the
informed path to the url. Note that this function will overwrite
the `url` field each time of call.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
