© 2026 Hedgehog Software, LLC

TwitterGitHubDiscord
More
CommunitiesDocsAboutTermsPrivacy
Search
Star
Setup for Free
SupabaseS
Supabase•8mo ago•
27 replies
Cryptish

Realtime postgres_changes not working in production

Hi

I'm using Nextjs 15 (app router), Supabase with Clerk integration and attempting to use realtime for my 'submissions' table.

Everything works as expected, when developing locally. In either case, I seem to be able to get the current token with no issues. Token then seems to get used as the apikey param in the wss:// url.

hook:
export function useSubmissions({ projectId, initialSubmissions }: UseSubmissionsProps) {
    const { user } = useUser();
    const { session } = useSession();
    const [submissions, setSubmissions] = useState<any[]>(initialSubmissions);

    useEffect(() => {
        if (!user || !session) return;

        const setupRealtime = async () => {
            const token = await session.getToken({ template: 'supabase' });
            console.log('Token:', token);
            if (!token) {
                console.error('Failed to get Clerk token for Supabase');
                return;
            }

            const supabase = createRealtimeClient(token);

            const channel = supabase
                .channel(`submissions:${projectId}`)
                .on('postgres_changes', {
                    event: 'INSERT',
                    schema: 'public',
                    table: 'submissions'
                }, (payload) => {
                    console.log('Submission received', payload);
                    const newSubmission = payload.new;
                    if (newSubmission?.status === 'pending' || newSubmission?.status === 'submitted') {
                        setSubmissions((current) => [...current, newSubmission]);
                    }
                })
                .on('postgres_changes', {
                    event: 'UPDATE',
                    schema: 'public',
                    table: 'submissions'
                }, (payload) => {
                    console.log('Submission updated', payload);
                    const updatedSubmission = payload.new;
                    setSubmissions((current) =>
                        current.map((submission) =>
                            submission.id === updatedSubmission.id ? updatedSubmission : submission
                        )
                    );
                })
                .on('postgres_changes', {
                    event: 'DELETE',
                    schema: 'public',
                    table: 'submissions'
                }, (payload) => {
                    console.log('Submission removed', payload);
                    const deletedSubmission = payload.old;
                    setSubmissions((current) =>
                        current.filter((submission) => submission.id !== deletedSubmission.id)
                    );
                })
                .subscribe((status, err) => {
                    console.log('📡 Subscription status:', status);
                    console.log('📡 Full error object:', err);

                    if (status === 'CHANNEL_ERROR') {
                        console.error('❌ CHANNEL_ERROR details:', {
                            error: err,
                            errorType: typeof err,
                            errorKeys: err ? Object.keys(err) : 'no error object'
                        });
                    }
                });

            return () => {
                channel.unsubscribe();
            };
        };

        setupRealtime();
    }, [user?.id, projectId, session?.id])

    return { submissions }
}
export function useSubmissions({ projectId, initialSubmissions }: UseSubmissionsProps) {
    const { user } = useUser();
    const { session } = useSession();
    const [submissions, setSubmissions] = useState<any[]>(initialSubmissions);

    useEffect(() => {
        if (!user || !session) return;

        const setupRealtime = async () => {
            const token = await session.getToken({ template: 'supabase' });
            console.log('Token:', token);
            if (!token) {
                console.error('Failed to get Clerk token for Supabase');
                return;
            }

            const supabase = createRealtimeClient(token);

            const channel = supabase
                .channel(`submissions:${projectId}`)
                .on('postgres_changes', {
                    event: 'INSERT',
                    schema: 'public',
                    table: 'submissions'
                }, (payload) => {
                    console.log('Submission received', payload);
                    const newSubmission = payload.new;
                    if (newSubmission?.status === 'pending' || newSubmission?.status === 'submitted') {
                        setSubmissions((current) => [...current, newSubmission]);
                    }
                })
                .on('postgres_changes', {
                    event: 'UPDATE',
                    schema: 'public',
                    table: 'submissions'
                }, (payload) => {
                    console.log('Submission updated', payload);
                    const updatedSubmission = payload.new;
                    setSubmissions((current) =>
                        current.map((submission) =>
                            submission.id === updatedSubmission.id ? updatedSubmission : submission
                        )
                    );
                })
                .on('postgres_changes', {
                    event: 'DELETE',
                    schema: 'public',
                    table: 'submissions'
                }, (payload) => {
                    console.log('Submission removed', payload);
                    const deletedSubmission = payload.old;
                    setSubmissions((current) =>
                        current.filter((submission) => submission.id !== deletedSubmission.id)
                    );
                })
                .subscribe((status, err) => {
                    console.log('📡 Subscription status:', status);
                    console.log('📡 Full error object:', err);

                    if (status === 'CHANNEL_ERROR') {
                        console.error('❌ CHANNEL_ERROR details:', {
                            error: err,
                            errorType: typeof err,
                            errorKeys: err ? Object.keys(err) : 'no error object'
                        });
                    }
                });

            return () => {
                channel.unsubscribe();
            };
        };

        setupRealtime();
    }, [user?.id, projectId, session?.id])

    return { submissions }
}


My realtime client:
export const createRealtimeClient = (token: string) => {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      auth: {
        persistSession: false
      },
      global: {
        headers: {
          Authorization: `Bearer ${token}`
        }
      },
      realtime: {
        params: {
          apikey: token,
          access_token: token
        }
      }
    }
  )
}
export const createRealtimeClient = (token: string) => {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      auth: {
        persistSession: false
      },
      global: {
        headers: {
          Authorization: `Bearer ${token}`
        }
      },
      realtime: {
        params: {
          apikey: token,
          access_token: token
        }
      }
    }
  )
}
Supabase banner
SupabaseJoin
Supabase gives you the tools, documentation, and community that makes managing databases, authentication, and backend infrastructure a lot less overwhelming.
45,816Members
Resources
Was this page helpful?

Similar Threads

Recent Announcements

Similar Threads

Realtime Postgres Changes vs Broadcast
SupabaseSSupabase / help-and-questions
11mo ago
Realtime Postgres changes trigger multiple times
SupabaseSSupabase / help-and-questions
8mo ago
Realtime - Postgres Changes - Updates stop coming through
SupabaseSSupabase / help-and-questions
3y ago
Error trying to connect to postgres_changes in realtime
SupabaseSSupabase / help-and-questions
5mo ago