supabase-rb-rb
Database

Call a Postgres function

Call a Postgres function (stored procedure). Supports POST, GET, and HEAD methods, plus chained filters on set-returning results.

Call a Postgres function over PostgREST. By default the function is invoked with POST and the body carries the arguments; get: true switches to a read-only GET (cacheable) and head: true issues a HEAD (count only, no body). The returned builder supports the same filters and modifiers as select — so set-returning functions can be filtered, ordered, and paged without a wrapping view.

Signature

supabase.rpc(func, params = {}, count: nil, head: false, get: false)

params defaults to {}. For POST calls it is serialized as the JSON request body; for GET / HEAD it is stringified and sent as query parameters (PostgREST then reads the function arguments from the URL).

Parameters

NameTypeRequiredDescription
funcStringRequiredPostgres function name. Resolved against the active schema (public by default, or whatever was set via supabase.schema(name)).
paramsHashOptionalFunction arguments keyed by parameter name. Sent as the JSON body on POST, or as URL query parameters on GET / HEAD.
countStringOptionalOne of "exact", "planned", "estimated". Adds Prefer: count=... so APIResponse#count is populated from the Content-Range header.
headBooleanOptionalWhen true, issues a HEAD request — no body, count only. Pair with count: to read a row total without fetching rows.
getBooleanOptionalWhen true, issues a GET request — arguments travel in the URL and the function can be cached by an HTTP proxy. Use for read-only functions marked STABLE or IMMUTABLE.

Returns

Returns
Supabase::Postgrest::RPCFilterRequestBuilder

A chainable builder that mixes in FilterMixin and SelectMixin, so set-returning functions support .eq, .gt, .order, .limit, .range, .single, .maybe_single, .csv, etc. before .execute. Calling .execute returns a SingleAPIResponse (PostgREST treats the call as scalar — data is whatever the function returned, parsed from JSON).

Example — scalar return

A function returning a single value (RETURNS integer, RETURNS text, RETURNS jsonb, …) comes back as response.data directly. No filters, no chaining — just .execute.

# CREATE FUNCTION add(a int, b int) RETURNS int AS $$
#   SELECT a + b;
# $$ LANGUAGE sql IMMUTABLE;

response = supabase.rpc("add", { a: 2, b: 3 }).execute
response.data  # => 5
# CREATE FUNCTION current_tenant() RETURNS text AS $$
#   SELECT current_setting('app.tenant_id', true);
# $$ LANGUAGE sql STABLE;

response = supabase.rpc("current_tenant").execute
response.data  # => "acme"

Example — set-returning function

A function declared RETURNS SETOF <table> or RETURNS TABLE(...) comes back as response.data of Array<Hash>. The shape mirrors what select returns for a table.

# CREATE FUNCTION countries_in_continent(target text)
#   RETURNS SETOF countries AS $$
#     SELECT * FROM countries WHERE continent = target;
# $$ LANGUAGE sql STABLE;

response = supabase.rpc("countries_in_continent", { target: "Africa" }).execute

response.data
# => [
#      { "id" => 1,  "name" => "Algeria",  "continent" => "Africa" },
#      { "id" => 54, "name" => "Zimbabwe", "continent" => "Africa" }
#    ]

Example — chained filters on a set-returning result

PostgREST applies filters, ordering, and pagination on top of the function's output. Chain them just like you would on a select — this is the killer feature of rpc for set-returning functions: you don't have to push the predicates into the function body.

response = supabase
  .rpc("countries_in_continent", { target: "Africa" })
  .gt("population", 50_000_000)
  .order("population", desc: true)
  .limit(5)
  .execute

response.data
# => [
#      { "id" => 23, "name" => "Nigeria", "population" => 220_000_000, ... },
#      { "id" => 14, "name" => "Ethiopia", "population" => 120_000_000, ... },
#      ...
#    ]

.single and .maybe_single also work, so a set-returning function with a unique filter can be coerced to a single object:

response = supabase
  .rpc("search_users", { q: "alice" })
  .eq("email", "alice@example.com")
  .single
  .execute

response.data  # => { "id" => 7, "email" => "alice@example.com", ... }

Example — count only with head: true

head: true issues a HEAD request — no body comes back, but PostgREST still computes the row count and returns it in the Content-Range header. Pair it with count: to read a total without paying for the rows.

response = supabase.rpc(
  "countries_in_continent",
  { target: "Africa" },
  count: "exact",
  head: true
).execute

response.data   # => nil (HEAD has no body)
response.count  # => 54

Example — get: true for cacheable read-only functions

For functions marked STABLE or IMMUTABLE, get: true switches the call to GET so the arguments travel in the URL and an HTTP cache (CDN, browser) can store the response. The function arguments must be JSON-stringifiable as query parameters.

response = supabase.rpc(
  "popular_tags",
  { limit: 10 },
  get: true
).execute

response.data  # => [{ "tag" => "ruby", "uses" => 1234 }, ...]

The builder is RPCFilterRequestBuilder — a SingleRequestBuilder subclass that mixes in FilterMixin and SelectMixin. Wire format: POST /rpc/<func> by default, GET / HEAD opt-in.

On this page