Long-running AI pipelines—think video processing, transcription, or machine learning tasks—pose a unique UX challenge: users want to know what’s happening, but the backend work can take minutes (or hours). Polling for status updates is inefficient and slow. Instead, real-time updates let users see the latest processing status instantly, keeping them informed and engaged. Supabase Realtime, built atop PostgreSQL’s logical replication, makes this easier than ever by streaming database changes directly to your frontend. Let’s walk through a practical approach to building live processing status updates using Supabase Realtime.
Why Real-Time Status Matters for AI Pipelines
Imagine you kick off a video analysis task in a web app. You want to see when the job starts, how far it’s progressed, and when it finishes—all without constantly refreshing the page. Real-time updates make your app feel alive and responsive, minimizing user frustration and support tickets.
For AI pipelines, processing times can be unpredictable. Real-time status indicators (like progress bars, logs, or completion notifications) set clear user expectations and let you surface actionable feedback (“Step 2/5: Running object detection…”).
The Basics: Supabase Realtime and Postgres Changes
Supabase Realtime lets you subscribe to changes in your Postgres database tables. When a row is inserted, updated, or deleted, your frontend gets notified instantly via WebSockets. This is perfect for tracking status fields or progress counters in a processing jobs table.
Key concepts:
-
Realtime subscriptions: Listen for
INSERT,UPDATE, orDELETEevents on a table. - Postgres row-level changes: Model status and progress directly in your database schema.
- Frontend listeners: React to change events and update your UI in real time.
Designing a Processing Jobs Table
Let’s start with a simple schema for tracking jobs. Each job represents a long-running AI process (e.g., a file being transcribed):
create table processing_jobs (
id uuid primary key default gen_random_uuid(),
user_id uuid not null,
input_file text not null,
status text not null default 'queued', -- 'queued', 'in_progress', 'completed', 'failed'
progress integer not null default 0, -- 0 to 100
result_url text,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now()
);
You’ll update the status and progress fields as the job advances. Supabase Realtime will notify any subscribers whenever these change.
Backend: Updating Status and Progress
Your backend (Node.js, Python, etc.) runs the AI pipeline and updates the job record as it proceeds:
// Example: Node.js backend using supabase-js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_KEY!);
async function updateJobStatus(jobId: string, status: string, progress: number) {
await supabase
.from('processing_jobs')
.update({ status, progress, updated_at: new Date().toISOString() })
.eq('id', jobId);
}
// At each pipeline step:
await updateJobStatus(jobId, 'in_progress', 10);
// ... do work ...
await updateJobStatus(jobId, 'in_progress', 70);
// ... final step ...
await updateJobStatus(jobId, 'completed', 100);
Make sure your backend updates the job row at each significant step in the pipeline.
Frontend: Listening for Real-Time Updates
Supabase’s JavaScript client lets you subscribe to row changes:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project.supabase.co', 'public-anon-key');
function listenToJob(jobId: string, onUpdate: (job: any) => void) {
return supabase
.channel('public:processing_jobs')
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'processing_jobs',
filter: `id=eq.${jobId}`,
},
(payload) => {
onUpdate(payload.new);
}
)
.subscribe();
}
// Usage: subscribe when the user views their job
const subscription = listenToJob(jobId, (job) => {
// Update your UI with new status/progress
setStatus(job.status);
setProgress(job.progress);
});
// Remember to unsubscribe when done
subscription.unsubscribe();
Now your UI will update in real time as the backend updates the job’s status and progress.
Practical Example: React Progress Bar
Here’s a basic React component that shows a live progress bar for a job:
import React, { useEffect, useState } from 'react';
import { createClient } from '@supabase/supabase-js';
const supabase = createClient('https://your-project.supabase.co', 'public-anon-key');
function ProcessingStatus({ jobId }: { jobId: string }) {
const [status, setStatus] = useState('queued');
const [progress, setProgress] = useState(0);
useEffect(() => {
const channel = supabase
.channel('public:processing_jobs')
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'processing_jobs',
filter: `id=eq.${jobId}`,
},
(payload) => {
setStatus(payload.new.status);
setProgress(payload.new.progress);
}
)
.subscribe();
return () => {
channel.unsubscribe();
};
}, [jobId]);
return (
<div>
<p>Status: {status}</p>
<progress value={progress} max={100}></progress>
<span>{progress}%</span>
</div>
);
}
This component will update the progress bar and status in real time as the job advances.
Handling Initial State and Error Cases
When a user opens the status page, you’ll want to fetch the job’s current state before listening for updates:
async function fetchJob(jobId: string) {
const { data, error } = await supabase
.from('processing_jobs')
.select('*')
.eq('id', jobId)
.single();
if (error) throw error;
return data;
}
Combine this with your real-time subscription for a seamless UX.
Don’t forget to handle errors—if the job fails, set status to 'failed' and show an appropriate message.
Security Considerations
Supabase Row Level Security (RLS) lets you control who can read which job records. For example, only the user who owns the job should be able to subscribe to its status:
-- Example RLS policy
create policy "Users can view their own jobs"
on processing_jobs
for select
using (user_id = auth.uid());
Make sure your policies allow subscription only to authorized users.
Scaling and Performance
Supabase Realtime uses logical replication, so every relevant database change is sent over WebSockets to all subscribed clients. For most SaaS or internal tooling scenarios, this is efficient and scalable. If you expect thousands of concurrent users or very high update frequency, consider:
- Throttling progress updates (send every few seconds, not every percent)
- Only subscribing to jobs the user is actually viewing
- Unsubscribing promptly when no longer needed
Beyond Progress Bars: Live Logs and Steps
You can extend this pattern to support richer feedback:
-
Step-by-step progress: Store a
current_stepor array of steps in the job row -
Live logs: Add a separate
processing_job_logstable and subscribe to new log rows -
Result delivery: When processing completes, update a
result_urlfield and trigger a frontend notification
Alternatives and Ecosystem
Supabase Realtime is just one approach to real-time processing status. Alternatives include:
- Firebase Realtime Database / Firestore: Similar real-time subscriptions, but not Postgres-based
- Socket.io or custom WebSocket servers: Full control, but more boilerplate
- Pusher, Ably, or other hosted pub/sub services: Managed websockets, but require integrating with your backend
Supabase shines when your main data lives in Postgres, and you want tight coupling between state changes and real-time notifications.
Key Takeaways
Building real-time status updates for long-running AI pipelines dramatically improves user experience. Supabase Realtime leverages Postgres row changes to provide instant UI feedback with minimal effort:
- Model job status and progress in your Postgres schema
- Update job rows from your backend pipeline as work proceeds
- Subscribe to table changes from your frontend for live updates
- Handle initial fetch, errors, and security with RLS policies
- Scale gracefully by limiting subscriptions and update frequency
With these building blocks, your users will always know exactly what’s happening—no refresh button required.
Top comments (0)