supabase-rb-rb
Realtime

Remove all channels

Unsubscribe every Realtime channel and close the socket.

supabase.remove_all_channels walks the registry, calls channel.unsubscribe on each, clears the registry, and disconnects the socket. Idempotent — a follow-up call on an empty registry is a no-op.

Use this at process shutdown, in test teardown, or any time you want the realtime client to release its socket and background threads (heartbeat, reconnect, read-loop).

Signature

supabase.remove_all_channels

The same method lives on the realtime sub-client (supabase.realtime.remove_all_channels) — the top-level shortcut forwards through dispatch_realtime.

Parameters

This method has no parameters.

Returns

Returns
self (Supabase::Realtime::Client) (sync) | Async::Task (async: true)

In sync mode the call blocks until every channel's phx_leave is queued and the socket is closed, then returns the realtime client itself. Under async: true the realtime teardown runs in a child Async task and the call returns the Async::Task so the calling fiber doesn't stall on N blocking Socket#send writes. Call .wait on the task to await completion.

Example — shut everything down

supabase.channel("public:countries").on_postgres_changes("*", schema: "public", table: "countries") { |p| handle(p) }.subscribe
supabase.channel("public:orders").on_postgres_changes("INSERT", schema: "public", table: "orders") { |p| dispatch(p) }.subscribe
supabase.channel("room:42").on_broadcast("chat:message") { |p| log(p) }.subscribe

# ...later...
supabase.remove_all_channels
# All three go through LEAVING → CLOSED, socket disconnects, background threads stop.

Example — clean teardown in a Rails initializer / on_exit

at_exit do
  supabase.remove_all_channels
end

Example — async: true returns an Async::Task

Under async: true the call returns an Async::Task. .wait on it inside a reactor.

supabase = Supabase.create_client(
  supabase_url: ENV.fetch("SUPABASE_URL"),
  supabase_key: ENV.fetch("SUPABASE_ANON_KEY"),
  async: true
)

# ...subscribe to a few channels...

Async do
  task = supabase.remove_all_channels
  task.wait
end

Outside a reactor, Async { } degrades to running inline — the call matches the sync path.

Iteration is over a snapshot

The implementation iterates @channels.dup so a channel that removes itself from the registry during its own unsubscribe (via handle_channel_close → _remove_channel) doesn't shift the live array mid-loop. After the loop, the registry is cleared and disconnect is called — an intentional close, not a bare @socket.close, so the background reconnect thread is stopped instead of immediately bringing the socket back up.

`remove_all_channels` returns `Async::Task` under `async: true`

The realtime client is thread-based and every channel's phx_leave is a blocking Socket#send; without dispatch, the calling fiber under async: true would hang while N writes drain serially. The umbrella's dispatch_realtime wraps the fan-out in an Async block so the call returns an Async::Task the caller .waits on. In sync mode (async: false, the default) the block runs straight through and the call returns self (the realtime client). Verified by spec/async/remove_channel_non_blocking_spec.rb.

On this page