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
| Name | Type | Required | Description |
|---|---|---|---|
func | String | Required | Postgres function name. Resolved against the active schema (public by default, or whatever was set via supabase.schema(name)). |
params | Hash | Optional | Function arguments keyed by parameter name. Sent as the JSON body on POST, or as URL query parameters on GET / HEAD. |
count | String | Optional | One of "exact", "planned", "estimated". Adds Prefer: count=... so APIResponse#count is populated from the Content-Range header. |
head | Boolean | Optional | When true, issues a HEAD request — no body, count only. Pair with count: to read a row total without fetching rows. |
get | Boolean | Optional | When 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
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 # => 54Example — 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.