Composable HTTP request builder and executor for Supabase services.
Requests are built as plain maps using a threading-friendly API, then executed synchronously via Hato. This is the backbone that all service modules (auth, storage, postgrest, functions, realtime) use internally.
A request map contains:
:method — HTTP method keyword (:get, :post, :put, :patch, :delete):url — fully resolved URL string:headers — map of header name to value:query — map of query parameter name to value:body — request body (map, string, or nil):service — originating service keyword (:auth, :storage, etc.):client — reference to the client map(require '[supabase.core.http :as http])
;; Build and execute a request
(-> (http/request client)
(http/with-service-url :auth-url "/signup")
(http/with-method :post)
(http/with-body {:email "user@example.com" :password "secret"})
(http/execute))
;; Returns {:status 200, :body {...}, :headers {...}} on success
;; Returns anomaly map on HTTP error (status >= 400)
;; Throwing variant for those who prefer exceptions
(-> (http/request client)
(http/with-service-url :auth-url "/signup")
(http/with-method :post)
(http/with-body {:email "user@example.com" :password "secret"})
(http/execute!))
;; Async variant returning CompletableFuture
(-> (http/request client)
(http/with-service-url :functions-url "/hello")
(http/with-method :post)
(http/with-body {:name "world"})
(http/execute-async))
Composable HTTP request builder and executor for Supabase services.
Requests are built as plain maps using a threading-friendly API, then
executed synchronously via Hato. This is the backbone that all service
modules (auth, storage, postgrest, functions, realtime) use internally.
## Request Map Structure
A request map contains:
- `:method` — HTTP method keyword (`:get`, `:post`, `:put`, `:patch`, `:delete`)
- `:url` — fully resolved URL string
- `:headers` — map of header name to value
- `:query` — map of query parameter name to value
- `:body` — request body (map, string, or nil)
- `:service` — originating service keyword (`:auth`, `:storage`, etc.)
- `:client` — reference to the client map
## Usage
(require '[supabase.core.http :as http])
;; Build and execute a request
(-> (http/request client)
(http/with-service-url :auth-url "/signup")
(http/with-method :post)
(http/with-body {:email "user@example.com" :password "secret"})
(http/execute))
;; Returns {:status 200, :body {...}, :headers {...}} on success
;; Returns anomaly map on HTTP error (status >= 400)
;; Throwing variant for those who prefer exceptions
(-> (http/request client)
(http/with-service-url :auth-url "/signup")
(http/with-method :post)
(http/with-body {:email "user@example.com" :password "secret"})
(http/execute!))
;; Async variant returning CompletableFuture
(-> (http/request client)
(http/with-service-url :functions-url "/hello")
(http/with-method :post)
(http/with-body {:name "world"})
(http/execute-async))(execute {:keys [service response-as] :as req})Executes the request synchronously via Hato.
Returns a response map on success (status < 400) or an anomaly map on error.
(-> (request client)
(with-service-url :auth-url "/token")
(with-method :post)
(with-body {:grant-type "password" :email "a@b.com" :password "x"})
(execute))
;; => {:status 200, :body {...}, :headers {...}}
Executes the request synchronously via Hato.
Returns a response map on success (status < 400) or an anomaly map on error.
(-> (request client)
(with-service-url :auth-url "/token")
(with-method :post)
(with-body {:grant-type "password" :email "a@b.com" :password "x"})
(execute))
;; => {:status 200, :body {...}, :headers {...}}(execute! req)Like execute, but throws an ex-info on error instead of returning an anomaly map.
The anomaly map is attached as the ex-data of the thrown exception.
(try
(-> (request client)
(with-service-url :auth-url "/token")
(with-method :post)
(execute!))
(catch Exception e
(ex-data e))) ;; => anomaly map
Like `execute`, but throws an `ex-info` on error instead of returning an anomaly map.
The anomaly map is attached as the ex-data of the thrown exception.
(try
(-> (request client)
(with-service-url :auth-url "/token")
(with-method :post)
(execute!))
(catch Exception e
(ex-data e))) ;; => anomaly map(execute-async {:keys [service response-as] :as req})Executes the request asynchronously, returning a CompletableFuture.
The future completes with the same value execute would return — either
a response map or an anomaly map.
@(-> (request client)
(with-service-url :functions-url "/hello")
(with-method :post)
(with-body {:name "world"})
(execute-async))
Executes the request asynchronously, returning a `CompletableFuture`.
The future completes with the same value `execute` would return — either
a response map or an anomaly map.
@(-> (request client)
(with-service-url :functions-url "/hello")
(with-method :post)
(with-body {:name "world"})
(execute-async))(merge-query-param req key value)(merge-query-param req key value sep)Appends value to the existing value at key in the query map, joined
by sep (default ","). When the key is absent, sets it to value.
Useful for PostgREST-style stacked filters where multiple values for the
same column must concatenate, not overwrite.
(merge-query-param req "order" "id.asc")
(merge-query-param req "order" "name.desc")
;; => {:query {"order" "id.asc,name.desc"}}
Appends `value` to the existing value at `key` in the query map, joined
by `sep` (default `","`). When the key is absent, sets it to `value`.
Useful for PostgREST-style stacked filters where multiple values for the
same column must concatenate, not overwrite.
(merge-query-param req "order" "id.asc")
(merge-query-param req "order" "name.desc")
;; => {:query {"order" "id.asc,name.desc"}}(request client)Initializes a request map from a client, pre-populating auth headers.
The request starts with :method :get and includes the client's global
headers plus authorization and apikey headers derived from the client.
(request client)
;; => {:method :get, :headers {...}, :client client, ...}
Initializes a request map from a client, pre-populating auth headers.
The request starts with `:method :get` and includes the client's global
headers plus `authorization` and `apikey` headers derived from the client.
(request client)
;; => {:method :get, :headers {...}, :client client, ...}(with-body req body)Sets the request body. Maps are JSON-encoded automatically. Strings and nil are passed through as-is.
(with-body req {:email "user@example.com"})
Sets the request body. Maps are JSON-encoded automatically.
Strings and nil are passed through as-is.
(with-body req {:email "user@example.com"})(with-headers req headers)Merges additional headers into the request. Later values override earlier ones for the same header name.
(with-headers req {"prefer" "return=representation"})
Merges additional headers into the request. Later values override earlier
ones for the same header name.
(with-headers req {"prefer" "return=representation"})(with-method req method)Sets the HTTP method for the request.
(with-method req :post)
Sets the HTTP method for the request. (with-method req :post)
(with-query req params)Merges query parameters into the request. Later values override earlier ones for the same parameter name.
(with-query req {"select" "*" "order" "id.asc"})
Merges query parameters into the request. Later values override earlier
ones for the same parameter name.
(with-query req {"select" "*" "order" "id.asc"})(with-response-as req as)Sets how Hato should coerce the response body. Defaults to :string,
which handle-response then JSON-decodes. Use :byte-array, :stream,
or :input-stream for binary responses (storage downloads). Non-string
bodies skip the JSON parse step.
(with-response-as req :byte-array)
Sets how Hato should coerce the response body. Defaults to `:string`,
which `handle-response` then JSON-decodes. Use `:byte-array`, `:stream`,
or `:input-stream` for binary responses (storage downloads). Non-string
bodies skip the JSON parse step.
(with-response-as req :byte-array)(with-service-url req service-url-key path)Sets the request URL by resolving a service URL key from the client and
appending path.
service-url-key is a keyword like :auth-url, :storage-url, etc.
The :service field is inferred from the key name.
(-> (request client)
(with-service-url :auth-url "/token"))
Sets the request URL by resolving a service URL key from the client and
appending `path`.
`service-url-key` is a keyword like `:auth-url`, `:storage-url`, etc.
The `:service` field is inferred from the key name.
(-> (request client)
(with-service-url :auth-url "/token"))cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |