supabase-rb-rb
Edge Functions

Invoke a Supabase Edge Function

Invoke a Supabase Edge Function by name. Always POSTs; body, headers, region routing, and response parsing are per-call kwargs.

Call a deployed Edge Function. invoke is always an HTTP POST against <base_url>/<function_name> — there is no method: kwarg (see the design note). The request body, custom headers, region routing, and response decoding are all per-call kwargs.

Signature

supabase.functions.invoke(function_name,body: nil,headers: {},region: nil,response_type: :text,return_response: false)

Parameters

NameTypeRequiredDescription
function_nameStringRequiredName of the deployed Edge Function (e.g. "hello-world"). Must be a non-empty String — anything else raises ArgumentError before the HTTP call.
bodyHash, Array, String, nilOptionalRequest payload. A Hash or Array is JSON-encoded and sent as application/json. A String is sent as-is with text/plain (override via headers:). nil sends no body. Any other class raises ArgumentError.
headersHashOptionalPer-invocation headers merged over the client-level defaults. Existing Content-Type set by the caller is respected (||=), NOT overwritten.
regionString, nilOptionalOne of Supabase::Functions::Types::FunctionRegion::ALL (e.g. "us-east-1") or FunctionRegion::ANY. Validated before the HTTP call — invalid regions raise ArgumentError. nil lets the server pick (no x-region header, no forceFunctionRegion query param).
response_typeSymbol, StringOptionalHow to decode the response body. :text (default) returns a UTF-8 String. :json parses the body as JSON and returns Hash/Array/scalar (raises on invalid JSON). :binary returns an ASCII-8BIT String byte-for-byte equal to the wire body. Parsing is opt-in by the caller — Content-Type is intentionally ignored.
return_responseBooleanOptionalDefault false. Pass true to receive the deprecated Types::Response wrapper (data + status + headers) instead of the bare body. Slated for removal — read data directly instead.

Returns

Returns
Object (Hash, Array, String, nil), or Supabase::Functions::Types::Response

By default (return_response: false), returns the decoded body: a Hash/Array/scalar when response_type: :json, a UTF-8 String when response_type: :text, or an ASCII-8BIT String when response_type: :binary. A nil response body comes through as nil for every response_type. When return_response: true, returns a Types::Response Struct with :data, :status, and :headers — this wrapper is deprecated and slated for removal.

Errors:

  • Supabase::Functions::Errors::FunctionsHttpError — raised on a non-2xx response from the function. Carries status and the message from the response body's "error" field (or a default message).
  • Supabase::Functions::Errors::FunctionsRelayError — raised when the Supabase relay layer signals failure via the x-relay-error: true response header. Distinguishable from FunctionsHttpError and rescued separately.

Example — JSON body, JSON response

require "supabase"

supabase = Supabase.create_client(
  ENV.fetch("SUPABASE_URL"),
  ENV.fetch("SUPABASE_ANON_KEY")
)

result = supabase.functions.invoke(
  "hello-world",
  body: { name: "Ada" },
  response_type: :json
)

result # => { "message" => "Hello, Ada!" }

body: { ... } is JSON-encoded and sent with Content-Type: application/json. response_type: :json parses the response body — a parse failure raises JSON::ParserError (the caller asked for JSON, so a non-JSON body is a contract violation).

Example — default text response

raw = supabase.functions.invoke("hello-world", body: { name: "Ada" })
raw           # => "{\"message\":\"Hello, Ada!\"}"
raw.encoding  # => #<Encoding:UTF-8>

Without response_type: :json, the body comes back as a raw UTF-8 String. The Content-Type response header is ignored — parsing is always opt-in by the caller.

Example — region routing

region = Supabase::Functions::Types::FunctionRegion::US_EAST_1

supabase.functions.invoke(
  "ingest-event",
  body: { user_id: 42, event: "signup" },
  region: region
)

Passing region: sets the x-region request header AND appends forceFunctionRegion=<region> to the URL query string. Region strings are validated before any HTTP call — a typo like "us-east-99" raises ArgumentError. Pass FunctionRegion::ANY (or "any") to explicitly request no region pinning. PascalCase aliases (FunctionRegion::UsEast1) also work.

Example — custom headers (trace ID, idempotency key)

supabase.functions.invoke(
  "charge-customer",
  body: { customer_id: 42, amount_cents: 1999 },
  headers: {
    "X-Trace-Id"        => SecureRandom.uuid,
    "Idempotency-Key"   => "charge-2026-06-12-customer-42-001"
  },
  response_type: :json
)

Per-invocation headers are merged over the client-level defaults (including Authorization). If you set Content-Type here it is respected — invoke only fills in Content-Type when the caller hasn't set one (||=).

Example — String body (already-serialized payload)

serialized = JSON.generate({ user_id: 42 })

supabase.functions.invoke(
  "ingest-prebaked",
  body: serialized,
  headers: { "Content-Type" => "application/json" },
  response_type: :json
)

A String body is sent as-is (no JSON-encoding). The default Content-Type is text/plain — override it via headers: if the wire format is something else.

Example — binary response (PDF, image, etc.)

bytes = supabase.functions.invoke(
  "render-invoice",
  body: { invoice_id: 4242 },
  response_type: :binary
)

bytes.encoding # => #<Encoding:ASCII-8BIT>
File.binwrite("invoice-4242.pdf", bytes)

response_type: :binary returns the body byte-for-byte with ASCII-8BIT (BINARY) encoding — no UTF-8 munging. The encoding choice is explicit at the call site.

Example — rescuing errors

begin
  supabase.functions.invoke("hello-world", body: { name: "Ada" }, response_type: :json)
rescue Supabase::Functions::Errors::FunctionsRelayError => e
  # The Supabase relay (in front of the function) errored. The function
  # itself may never have executed.
  warn "relay error: #{e.message} (status #{e.status})"
rescue Supabase::Functions::Errors::FunctionsHttpError => e
  # Non-2xx from the function. e.message is parsed from the response
  # body's "error" field when present.
  warn "function returned #{e.status}: #{e.message}"
end

Both error classes inherit from Supabase::Functions::Errors::FunctionsError — rescue that to catch any Functions-API failure.

Streaming responses

Streaming responses are not supported. invoke reads the full response body into memory before returning — there is no callback, block, or IO-yielding form analogous to supabase-js's Response.body ReadableStream. The underlying Faraday session does not expose a streaming reader, and :text / :binary / :json all consume response.body whole.

If your function returns a large payload, prefer:

  1. response_type: :binary so the bytes are not re-encoded as UTF-8 (cheap copy, no transcoding cost).
  2. A higher per-request timeout via the client-level timeout: kwarg — the default is 60 seconds.
  3. A direct Faraday connection injected via http_client: if you really need streaming semantics — you can configure the adapter to handle large responses outside invoke. See the Edge Functions README for the injection pattern.

If full streaming support lands, this section will be updated.

No `method:` or `query:` kwarg

invoke is always a POST, and the only query-string consumer is region routing (handled internally via forceFunctionRegion). Other design choices worth knowing: region is validated before the HTTP call (raises ArgumentError on typos rather than warning-and-coercing); a caller-provided Content-Type is respected via ||= instead of being overwritten; x-relay-error is read case-insensitively per RFC 7230; and response_type: :json raises on invalid JSON (a contract violation) rather than silently falling back to the raw String.

On this page