Client configuration for interacting with Supabase.
Creates and manages immutable client maps containing connection options, service URLs, and configuration for your Supabase project. The client is a plain Clojure map validated with Malli schemas.
(require '[supabase.core.client :as client])
;; Create a client with defaults
(client/make-client "https://abc.supabase.co" "my-api-key")
;; Create a client with options
(client/make-client "https://abc.supabase.co" "my-api-key"
:db {:schema "another"}
:auth {:flow-type "pkce"}
:global {:headers {"custom-header" "custom-value"}})
;; Update the access token for authenticated requests
(client/update-access-token client "new-token")
{:base-url "https://abc.supabase.co"
:api-key "my-api-key"
:access-token "my-api-key"
:auth-url "https://abc.supabase.co/auth/v1"
:database-url "https://abc.supabase.co/rest/v1"
:storage-url "https://abc.supabase.co/storage/v1"
:functions-url "https://abc.supabase.co/functions/v1"
:realtime-url "https://abc.supabase.co/realtime/v1"
:db {:schema "public"}
:global {:headers {"x-client-info" "supabase-clj/0.2.0"}}
:auth {:auto-refresh-token true, :flow-type "implicit", ...}
:storage {:use-new-hostname false}}
See https://supabase.com/docs/reference/javascript/initializing
Client configuration for interacting with Supabase.
Creates and manages immutable client maps containing connection options,
service URLs, and configuration for your Supabase project. The client is
a plain Clojure map validated with Malli schemas.
## Usage
(require '[supabase.core.client :as client])
;; Create a client with defaults
(client/make-client "https://abc.supabase.co" "my-api-key")
;; Create a client with options
(client/make-client "https://abc.supabase.co" "my-api-key"
:db {:schema "another"}
:auth {:flow-type "pkce"}
:global {:headers {"custom-header" "custom-value"}})
;; Update the access token for authenticated requests
(client/update-access-token client "new-token")
## Client Map Structure
{:base-url "https://abc.supabase.co"
:api-key "my-api-key"
:access-token "my-api-key"
:auth-url "https://abc.supabase.co/auth/v1"
:database-url "https://abc.supabase.co/rest/v1"
:storage-url "https://abc.supabase.co/storage/v1"
:functions-url "https://abc.supabase.co/functions/v1"
:realtime-url "https://abc.supabase.co/realtime/v1"
:db {:schema "public"}
:global {:headers {"x-client-info" "supabase-clj/0.2.0"}}
:auth {:auto-refresh-token true, :flow-type "implicit", ...}
:storage {:use-new-hostname false}}
See https://supabase.com/docs/reference/javascript/initializingAnomaly-based error handling for the Supabase Clojure SDK.
Errors are represented as plain maps following the cognitect/anomalies convention. This avoids tagged tuples and exceptions by default, matching the data-driven philosophy of libraries like cognitect/aws-api.
The SDK maps HTTP status codes and domain errors to these categories:
:cognitect.anomalies/incorrect — bad request, validation failure (4xx client errors):cognitect.anomalies/forbidden — authentication/authorization failure (401, 403):cognitect.anomalies/not-found — resource not found (404):cognitect.anomalies/conflict — resource already exists (409):cognitect.anomalies/busy — rate limited, resource locked (423, 429):cognitect.anomalies/unavailable — server error, service unavailable (5xx):cognitect.anomalies/fault — unexpected server-side failureAn anomaly map always contains :cognitect.anomalies/category and may include:
:cognitect.anomalies/message — human-readable error description:supabase/service — originating service (:auth, :storage, etc.):supabase/code — semantic error code keyword (e.g. :not-found):http/status — original HTTP status code:http/body — response body (parsed or raw):http/headers — response headers(require '[supabase.core.error :as error])
;; Check if a result is an error
(error/anomaly? result)
;; Create an anomaly from an HTTP response
(error/from-http-response 404 {:message "Not Found"} :storage)
;; Create a domain-specific anomaly
(error/anomaly :cognitect.anomalies/incorrect
{:supabase/service :auth
:cognitect.anomalies/message "Invalid credentials"})
Anomaly-based error handling for the Supabase Clojure SDK.
Errors are represented as plain maps following the cognitect/anomalies convention.
This avoids tagged tuples and exceptions by default, matching the data-driven
philosophy of libraries like cognitect/aws-api.
## Anomaly Categories
The SDK maps HTTP status codes and domain errors to these categories:
- `:cognitect.anomalies/incorrect` — bad request, validation failure (4xx client errors)
- `:cognitect.anomalies/forbidden` — authentication/authorization failure (401, 403)
- `:cognitect.anomalies/not-found` — resource not found (404)
- `:cognitect.anomalies/conflict` — resource already exists (409)
- `:cognitect.anomalies/busy` — rate limited, resource locked (423, 429)
- `:cognitect.anomalies/unavailable` — server error, service unavailable (5xx)
- `:cognitect.anomalies/fault` — unexpected server-side failure
## Structure
An anomaly map always contains `:cognitect.anomalies/category` and may include:
- `:cognitect.anomalies/message` — human-readable error description
- `:supabase/service` — originating service (`:auth`, `:storage`, etc.)
- `:supabase/code` — semantic error code keyword (e.g. `:not-found`)
- `:http/status` — original HTTP status code
- `:http/body` — response body (parsed or raw)
- `:http/headers` — response headers
## Usage
(require '[supabase.core.error :as error])
;; Check if a result is an error
(error/anomaly? result)
;; Create an anomaly from an HTTP response
(error/from-http-response 404 {:message "Not Found"} :storage)
;; Create a domain-specific anomaly
(error/anomaly :cognitect.anomalies/incorrect
{:supabase/service :auth
:cognitect.anomalies/message "Invalid credentials"})Composable HTTP request builder and executor for Supabase services.
Requests are built as plain maps using a threading-friendly API, then
executed through a supabase.core.transport/Transport. The default
transport wraps Hato; tests and integrators can swap it.
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, bytes, File, InputStream, or nil):multipart — vector of multipart parts (mutually exclusive with :body):response-as — :string (default), :byte-array, :stream, :reader:decoder — fn from raw body to parsed body (default JSON for :string):error-parser — fn [status body headers service] → anomaly map:log? — emit debug/error log lines for this request:timeout — per-request timeout (ms):transport — explicit transport instance (overrides client transport):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))
;; => {:status 200, :body {...}, :headers {...}} on success
;; => anomaly map on HTTP error (status >= 400)
;; Streaming response (no decoding)
(-> (http/request client)
(http/with-service-url :storage-url "/object/bucket/path")
(http/with-response-as :stream)
(http/execute))
;; => {:status 200, :body #object[java.io.InputStream ...], :headers {...}}
;; Multipart upload
(-> (http/request client)
(http/with-service-url :storage-url "/object/bucket/path")
(http/with-method :post)
(http/with-multipart [{:name "file" :content (io/file "a.png")
:content-type "image/png" :filename "a.png"}])
(http/execute))
Composable HTTP request builder and executor for Supabase services.
Requests are built as plain maps using a threading-friendly API, then
executed through a [[supabase.core.transport/Transport]]. The default
transport wraps Hato; tests and integrators can swap it.
## Request map
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, bytes, File, InputStream, or nil)
- `:multipart` — vector of multipart parts (mutually exclusive with `:body`)
- `:response-as` — `:string` (default), `:byte-array`, `:stream`, `:reader`
- `:decoder` — fn from raw body to parsed body (default JSON for `:string`)
- `:error-parser` — fn `[status body headers service]` → anomaly map
- `:log?` — emit debug/error log lines for this request
- `:timeout` — per-request timeout (ms)
- `:transport` — explicit transport instance (overrides client transport)
- `: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))
;; => {:status 200, :body {...}, :headers {...}} on success
;; => anomaly map on HTTP error (status >= 400)
;; Streaming response (no decoding)
(-> (http/request client)
(http/with-service-url :storage-url "/object/bucket/path")
(http/with-response-as :stream)
(http/execute))
;; => {:status 200, :body #object[java.io.InputStream ...], :headers {...}}
;; Multipart upload
(-> (http/request client)
(http/with-service-url :storage-url "/object/bucket/path")
(http/with-method :post)
(http/with-multipart [{:name "file" :content (io/file "a.png")
:content-type "image/png" :filename "a.png"}])
(http/execute))Pluggable HTTP transport for the Supabase Clojure SDK.
All HTTP traffic is executed through an implementation of the
Transport protocol. The default implementation is a thin wrapper
around hato.client; service modules (auth, storage, postgrest,
functions, realtime) never call Hato directly.
Why a protocol? Three reasons:
HttpClient instance with custom timeouts, pool sizing, and
HTTP version selection.Transport and inject it via the
client map (:transport) or per request.A transport receives a lower-level request map produced by
supabase.core.http:
{:method :post
:url "https://abc.supabase.co/auth/v1/token"
:headers {"authorization" "Bearer ..." ...}
:query-params {"grant_type" "password"}
:body "{\"email\":\"a@b.com\"}" ;; string | bytes | File | InputStream
:multipart [{:name "file" :content #object[File ...]
:content-type "image/png" :file-name "a.png"}]
:as :string ;; :string | :byte-array | :stream | :reader
:timeout 30000} ;; optional
The transport must return a map with at minimum:
{:status 200
:headers {"content-type" "application/json" ...}
:body "{...}" ;; type depends on :as
:uri "..." ;; optional, but recommended for logging
:request {...}} ;; optional echo of the request for debugging
Exceptions are caught by supabase.core.http, not by the transport,
and converted to anomalies via supabase.core.error/from-exception.
execute-async returns a java.util.concurrent.CompletionStage (or
CompletableFuture) that resolves to a response map. The same
exception handling rules apply.
Pluggable HTTP transport for the Supabase Clojure SDK.
All HTTP traffic is executed through an implementation of the
[[Transport]] protocol. The default implementation is a thin wrapper
around `hato.client`; service modules (auth, storage, postgrest,
functions, realtime) never call Hato directly.
Why a protocol? Three reasons:
1. **Testability** — tests inject a fake transport that returns
canned responses without touching the network.
2. **Pooling** — a per-client transport can own a single
`HttpClient` instance with custom timeouts, pool sizing, and
HTTP version selection.
3. **Adapters** — callers who need clj-http, http-kit, or a custom
HTTP stack can implement [[Transport]] and inject it via the
client map (`:transport`) or per request.
## Request format
A transport receives a lower-level request map produced by
`supabase.core.http`:
{:method :post
:url "https://abc.supabase.co/auth/v1/token"
:headers {"authorization" "Bearer ..." ...}
:query-params {"grant_type" "password"}
:body "{\"email\":\"a@b.com\"}" ;; string | bytes | File | InputStream
:multipart [{:name "file" :content #object[File ...]
:content-type "image/png" :file-name "a.png"}]
:as :string ;; :string | :byte-array | :stream | :reader
:timeout 30000} ;; optional
## Response format
The transport must return a map with at minimum:
{:status 200
:headers {"content-type" "application/json" ...}
:body "{...}" ;; type depends on :as
:uri "..." ;; optional, but recommended for logging
:request {...}} ;; optional echo of the request for debugging
Exceptions are caught by `supabase.core.http`, not by the transport,
and converted to anomalies via `supabase.core.error/from-exception`.
## Async
`execute-async` returns a `java.util.concurrent.CompletionStage` (or
`CompletableFuture`) that resolves to a response map. The same
exception handling rules apply.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 |