saascode

Debug Supabase Realtime from publication to browser

A connected Supabase channel can still deliver nothing. Check publication membership, RLS, event requirements, and the browser subscription lifecycle.

academy · aug 21, 2026 · 2 min read

The most expensive Realtime failure is a subscription that appears connected and never fires. There is no obvious crash. The screen is simply stale.

Every Realtime feature in one live build failed that way. The initial diagnosis bundled three requirements together, but current Supabase behavior makes an important distinction: publication membership is required for Postgres Changes; REPLICA IDENTITY FULL is conditional; and the client must live in a runtime that can keep the subscription alive.

Debug those layers in that order.

1. Is the table being published?

For Postgres Changes, enable the table under the supabase_realtime publication or add it in a migration:

alter publication supabase_realtime
  add table public.messages;

Make that migration idempotent in the style used by your project, and avoid adding the same table twice. Supabase now recommends Broadcast for many scalable database-change use cases, but Postgres Changes remains the simpler option for modest workloads.

2. May this subscriber read the row?

With RLS enabled, Realtime only sends Postgres Changes records a subscriber is allowed to read. Confirm the client has a current session, the table grants include SELECT for the relevant role, and the RLS policy permits the row.

Test with two users from different tenants. “The owner receives the event” and “the other tenant does not” are both required assertions.

Do not add REPLICA IDENTITY FULL automatically. Use it when you need old row values on updates or deletes, or when your delete-filter behavior requires it:

alter table public.messages replica identity full;

Full replica identity increases the data written to the WAL. It is a tradeoff, not a universal setup step.

3. Does the subscription survive?

Create and subscribe to the channel in the browser or another long-lived runtime. A request-scoped server client cannot keep a WebSocket alive after the request ends. The package name alone does not decide this: @supabase/ssr provides both server and browser client factories, and a browser client ultimately uses supabase-js Realtime.

In React, subscribe inside an effect and remove the channel on cleanup. Log channel status while debugging, and surface a degraded state instead of pretending stale data is current.

Prove the entire path

Insert a known row after the channel reports subscribed. Record whether the database change occurred, the row satisfied RLS for the subscriber, the table belonged to the publication, and the callback ran once. Repeat for UPDATE and DELETE only if the feature needs them.

This turns “Realtime is broken” into four smaller questions: was the change written, published, authorized, and received? The original build missed several layers at once. Your diagnostic should make each layer independently visible.

end