Supabase.Fetcher.Request (supabase_potion v0.6.1)

Supabase.Fetcher.Request is a structure to handle HTTP request builder designed to interface seamlessly with Supabase services.

Key Features

  • Request Composition: Build requests with method, headers, body, and query parameters using a composable builder pattern.
  • Service-Specific Integrations: Automatically derive URLs for Supabase services like auth, functions, storage, realtime, and database.
  • Customizable Response Handling: Attach decoders and error parsers tailored to specific service requirements.
  • Error Management: Centralized error handling through Supabase.ErrorParser, supporting structured and semantic error reporting.

Key Components

Request Builder API

The Supabase.Fetcher.Request provides a composable API for constructing HTTP requests. Each step updates the request builder state:

  • with_<service>_url/2: Appends the path to a service-specific base URL, available services can be consulted on Supabase.services() typespec.
  • with_method/2: Sets the HTTP method (:get, :post, etc.).
  • with_headers/2: Appends or overrides headers.
  • with_body/2: Sets the request body, supporting JSON, iodata, or streams.
  • with_query/2: Adds query parameters.
  • with_body_decoder/3: Registers a custom body decoder to be hooked into the response, defaults to Supabase.Fetcher.JSONDecoder.
  • with_error_parser/2: Registers a custom error parser to be hooked into the response, defaults to Supabase.ErrorParser.

Custom HTTP Clients

Supabase.Fetcher.Request depends on a Supabase.Fetcher.Adapter implementation to dispatch HTTP requests, check Supabase.Fetcher module documentation for more info.

Decoders and Parsers

  • Body Decoder: Custom modules implementing the Supabase.Fetcher.BodyDecoder behaviour can decode response bodies into application-specific formats.
  • Error Parser: Handle service-specific errors using Supabase.Error implementations, ensuring consistent error reporting across services.

Example Usage

Basic Request

{: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()

Custom Decoders and Error Parsers

{:ok, response} =
  Supabase.Fetcher.new(client)
  |> Supabase.Fetcher.with_functions_url("/execute")
  |> Supabase.Fetcher.with_body_decoder(MyCustomDecoder)
  |> Supabase.Fetcher.with_error_parser(MyErrorParser)
  |> Supabase.Fetcher.request()

Notes

This module is designed to be extensible and reusable for all Supabase-related services. It abstracts away the low-level HTTP intricacies while providing the flexibility developers need to interact with Supabase services in Elixir applications.

Summary

Functions

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.

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.

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.

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.

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.

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.

Defines the request body to be sent, it can be a map, that will be encoded with Jason.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.

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/1 callback interface.

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.

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.

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.

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.

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

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.

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.

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.

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.

Types

options()

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

t()

@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(),
  method: Supabase.Fetcher.method(),
  options: options(),
  query: Supabase.Fetcher.query(),
  service: Supabase.service(),
  url: Supabase.Fetcher.url()
}

Functions

get_header(builder, key, default \\ nil)

@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(builder, key, default \\ nil)

@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(builder, key, value, list \\ [with: ","])

@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(builder, key, value, list \\ [with: ","])

@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(client, opts \\ [])

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(builder, path)

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(builder, body \\ nil)

Defines the request body to be sent, it can be a map, that will be encoded with Jason.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.

with_body_decoder(builder, decoder, decoder_opts \\ [])

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/1 callback interface.

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

with_database_url(builder, path)

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(builder, 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(builder, path)

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(builder, 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(builder, adapter)

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(builder, method \\ :get)

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(builder, 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(builder, path)

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(builder, path)

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.