DEV Community

albert nahas
albert nahas

Posted on • Originally published at leandine.hashnode.dev

Building Real-Time Processing Status with Supabase Realtime

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, or DELETE events 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()
);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode

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_step or array of steps in the job row
  • Live logs: Add a separate processing_job_logs table and subscribe to new log rows
  • Result delivery: When processing completes, update a result_url field 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)