Initializing
Install supabase-rb and construct a Supabase::Client.
supabase-rb is a single gem that bundles Auth, PostgREST (Database), Storage, Edge Functions, and Realtime behind one umbrella client. Supabase.create_client is the entry point — pass your project URL and an API key, get back a Supabase::Client.
Install
Add the gem to your Gemfile:
# Gemfile
gem "supabase-rb"Then run:
bundle installOr install it directly without bundler:
gem install supabase-rbRequires Ruby ≥ 3.1.
First query
A complete end-to-end example — install the gem, construct a client with your project URL + anon key, and read a row:
require "supabase"
supabase = Supabase.create_client(
supabase_url: ENV.fetch("SUPABASE_URL"),
supabase_key: ENV.fetch("SUPABASE_ANON_KEY")
)
response = supabase.from("countries").select("*").limit(1).execute
puts response.data
# => [{"id" => 1, "name" => "United Kingdom"}]SUPABASE_URL is your project URL (e.g. https://abcd1234.supabase.co) and SUPABASE_ANON_KEY is the anon (anon) key from the project's API settings.
Construct a client
Supabase.create_client(supabase_url:, supabase_key:, options: {}, async: false)Equivalent to Supabase::Client.new(...) but routes through Client.create, which additionally restores a persisted session (if options.persist_session is enabled and the auth storage holds one) and applies the session's access token as the bearer.
| Name | Type | Required | Description |
|---|---|---|---|
supabase_url | String | Required | Your project URL, e.g. https://abcd1234.supabase.co. |
supabase_key | String | Required | A project API key — anon for client-side use, service-role for trusted server-side use. |
options | Supabase::ClientOptions | Hash | Optional | Configuration. Accepts a ClientOptions instance, a flat hash of ClientOptions fields, or the legacy nested-hash form. |
async | Boolean | Optional | When true, swaps every HTTP sub-client to the async-http-faraday variant. Defaults to false. |
An umbrella client exposing auth, postgrest (via from/table/rpc/schema), storage, functions, and realtime (via channel).
supabase = Supabase.create_client(
supabase_url: "https://abcd1234.supabase.co",
supabase_key: ENV.fetch("SUPABASE_ANON_KEY")
)You can also call Supabase::Client.new(...) directly if you want to skip the persisted-session restore:
supabase = Supabase::Client.new(
supabase_url: "https://abcd1234.supabase.co",
supabase_key: ENV.fetch("SUPABASE_ANON_KEY")
)ClientOptions
Supabase::ClientOptions is the typed configuration struct passed via options:. A single class covers both sync and async modes because the sync/async split is decided at runtime via the umbrella async: flag (see Async mode).
Supabase::ClientOptions.new(schema: "public",headers: nil,auto_refresh_token: true,persist_session: true,realtime: nil,postgrest_client_timeout: 120,storage_client_timeout: 20,function_client_timeout: 5,flow_type: "pkce",storage: nil,http_client: nil)| Name | Type | Required | Description |
|---|---|---|---|
schema | String | Optional | Default Postgres schema for PostgREST requests. Defaults to "public". Use client.schema("other") to query another schema without mutating this default. |
headers | Hash<String, String> | Optional | Global headers merged into every HTTP sub-client (auth, postgrest, storage, functions). Useful for X-Tenant, X-Request-Id, or overriding Authorization. |
auto_refresh_token | Boolean | Optional | When true (default), the auth client schedules a background timer to refresh the access token before it expires. |
persist_session | Boolean | Optional | When true (default), the auth client writes the session to the configured storage backend so it survives process restarts. |
realtime | Hash | Optional | Keyword overrides forwarded verbatim to Realtime::Client (e.g. { transport: ..., logger: ..., timeout: 10 }). |
postgrest_client_timeout | Integer (seconds) | Optional | Request timeout for PostgREST calls. Defaults to 120. |
storage_client_timeout | Integer (seconds) | Optional | Request timeout for Storage calls. Defaults to 20. |
function_client_timeout | Integer (seconds) | Optional | Request timeout for Edge Functions calls. Defaults to 5. |
flow_type | String | Optional | OAuth flow type for the auth client. Defaults to "pkce". |
storage | Object | Optional | Session storage backend. Any object responding to get_item / set_item / remove_item. Defaults to in-memory storage. |
http_client | Faraday::Connection | Optional | Optional pre-built Faraday connection shared by all sub-clients. Lets you wire your own middleware, adapter, or instrumentation. |
options = Supabase::ClientOptions.new(
schema: "public",
headers: { "X-Tenant" => "acme" },
auto_refresh_token: true,
persist_session: true,
postgrest_client_timeout: 30,
storage_client_timeout: 20,
function_client_timeout: 10,
flow_type: "pkce"
)
supabase = Supabase.create_client(
supabase_url: ENV.fetch("SUPABASE_URL"),
supabase_key: ENV.fetch("SUPABASE_ANON_KEY"),
options: options
)A flat hash of the same keys works equivalently — it is canonicalized into a ClientOptions instance internally:
supabase = Supabase.create_client(
supabase_url: ENV.fetch("SUPABASE_URL"),
supabase_key: ENV.fetch("SUPABASE_ANON_KEY"),
options: { schema: "public", headers: { "X-Tenant" => "acme" } }
)Global headers
Headers passed via options.headers are merged into every HTTP sub-client. The default X-Client-Info: supabase-rb/<version> header is always included so your project logs can attribute requests to the Ruby gem.
options = Supabase::ClientOptions.new(
headers: { "X-Tenant" => "acme", "X-Request-Id" => SecureRandom.uuid }
)Database schema
schema: sets the default Postgres schema for all PostgREST requests. To query a non-default schema for a single chain without changing this default, use client.schema("other"):
supabase.schema("private").from("audit_log").select("*").executeAsync mode
Pass async: true to swap every HTTP sub-client (auth, postgrest, storage, functions) to its async-http-faraday variant. Calls return values that integrate with the async gem's reactor.
require "async"
require "supabase"
Async do
supabase = Supabase.create_client(
supabase_url: ENV.fetch("SUPABASE_URL"),
supabase_key: ENV.fetch("SUPABASE_ANON_KEY"),
async: true
)
response = supabase.from("countries").select("*").execute
puts response.data
endOne client, runtime async flag
Async mode is opt-in at construction time via async: true — there is no separate AsyncClient class. Request builders are transport-agnostic, so the same call sites work in both modes.
Two things to know:
- Under
async: true,client.remove_channel/client.remove_all_channels(and theset_authfan-out to a connected realtime socket) return anAsync::Taskinstead of blocking the calling fiber — call.waiton it to await the result. - The realtime client itself stays thread-based regardless of
async:— only the HTTP sub-clients switch transport. The umbrella'sdispatch_realtimehelper bridges the two worlds for you.
Legacy nested-hash options
The original Ruby options shape — a nested hash keyed by sub-client name — is still accepted alongside ClientOptions. Existing callers don't need to migrate:
supabase = Supabase.create_client(
supabase_url: ENV.fetch("SUPABASE_URL"),
supabase_key: ENV.fetch("SUPABASE_ANON_KEY"),
options: {
auth: { auto_refresh_token: true, persist_session: true, flow_type: "pkce" },
postgrest: { schema: "public", timeout: 30 },
storage: { timeout: 20 },
functions: { timeout: 10 },
realtime: { logger: Logger.new($stdout) },
global: { headers: { "X-Tenant" => "acme" } }
}
)The shape is detected automatically — presence of any of :auth, :postgrest, :functions, :global, or a Hash-valued :storage switches the client into legacy-routing mode.
Prefer ClientOptions for new code
The legacy nested-hash form is preserved for backwards compatibility with supabase-rb ≤ 3.x, when there was no equivalent of ClientOptions. New code should prefer ClientOptions (or a flat hash of its fields) — the legacy shape silently drops any ClientOptions-style keys mixed in, and the client emits a warning when it detects such stray keys.