Next.jsSupabaseReal-TimeAIWebSockets

Designing Real-Time AI Systems with Next.js and Supabase

10 min read
Designing Real-Time AI Systems with Next.js and Supabase

A deep dive into building responsive, real-time AI applications. I explore how I use modern full-stack tools to handle live data, user interactions, and AI responses—while maintaining performance, scalability, and a seamless user experience.

Introduction

Real-time features are no longer optional in modern applications. Users expect instant feedback, live updates, and seamless interactions. When you add AI into the mix, the complexity multiplies—but so does the potential.

In this article, I'll walk through how I architect real-time AI systems using Next.js and Supabase, covering everything from database design to streaming AI responses.

Why This Stack?

The Next.js + Supabase combination is powerful for several reasons:

- Next.js App Router - Server components, streaming, and edge functions

- Supabase Realtime - Built-in WebSocket subscriptions for live data

- Supabase Auth - Row-level security that works with real-time

- Edge Functions - Run AI inference close to users

- Postgres - Full SQL power with real-time capabilities

Architecture for Real-Time AI

text
┌─────────────────────────────────────────────────────────┐
│                    Next.js Frontend                      │
│  ┌─────────────────┐  ┌─────────────────────────────┐   │
│  │ React Components │  │ Supabase Realtime Client   │   │
│  └────────┬────────┘  └──────────────┬──────────────┘   │
│           │                          │                   │
│           ▼                          ▼                   │
│  ┌─────────────────────────────────────────────────┐    │
│  │              Server Actions / API Routes         │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                      Supabase                            │
│  ┌──────────┐  ┌──────────────┐  ┌────────────────┐     │
│  │ Postgres │◄─│   Realtime   │  │ Edge Functions │     │
│  │          │  │  (WebSocket) │  │   (AI Logic)   │     │
│  └──────────┘  └──────────────┘  └────────────────┘     │
└─────────────────────────────────────────────────────────┘

Setting Up Real-Time Subscriptions

First, let's set up the Supabase client with real-time capabilities:

typescript
// lib/supabase/client.ts
import { createBrowserClient } from "@supabase/ssr";

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
}

Now, create a hook for real-time message subscriptions:

typescript
// hooks/useRealtimeMessages.ts
import { useEffect, useState } from "react";
import { createClient } from "@/lib/supabase/client";
import { Message } from "@/types";

export function useRealtimeMessages(conversationId: string) {
  const [messages, setMessages] = useState<Message[]>([]);
  const supabase = createClient();

  useEffect(() => {
    // Fetch initial messages
    const fetchMessages = async () => {
      const { data } = await supabase
        .from("messages")
        .select("*")
        .eq("conversation_id", conversationId)
        .order("created_at", { ascending: true });
      
      if (data) setMessages(data);
    };

    fetchMessages();

    // Subscribe to new messages
    const channel = supabase
      .channel(`messages:${conversationId}`)
      .on(
        "postgres_changes",
        {
          event: "INSERT",
          schema: "public",
          table: "messages",
          filter: `conversation_id=eq.${conversationId}`,
        },
        (payload) => {
          setMessages((prev) => [...prev, payload.new as Message]);
        }
      )
      .subscribe();

    return () => {
      supabase.removeChannel(channel);
    };
  }, [conversationId]);

  return messages;
}

Streaming AI Responses

The key to a great AI UX is streaming responses token-by-token:

typescript
// app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
import { createClient } from "@/lib/supabase/server";

export async function POST(req: Request) {
  const { messages, conversationId } = await req.json();
  const supabase = createClient();

  // Create a placeholder message in the database
  const { data: aiMessage } = await supabase
    .from("messages")
    .insert({
      conversation_id: conversationId,
      role: "assistant",
      content: "",
      status: "streaming",
    })
    .select()
    .single();

  // Stream the response
  const result = await streamText({
    model: openai("gpt-4-turbo"),
    messages,
    onFinish: async ({ text }) => {
      // Update the message when complete
      await supabase
        .from("messages")
        .update({ content: text, status: "complete" })
        .eq("id", aiMessage.id);
    },
  });

  return result.toDataStreamResponse();
}

Performance Optimizations

### 1. Debounce Database Updates

Don't update the database on every token:

typescript
import { debounce } from "lodash";

const debouncedUpdate = debounce(async (messageId: string, content: string) => {
  await supabase.from("messages").update({ content }).eq("id", messageId);
}, 500);

### 2. Use Edge Functions for AI

Deploy AI logic to the edge for lower latency.

Conclusion

Building real-time AI systems requires careful orchestration of multiple technologies. The Next.js + Supabase stack provides all the primitives you need:

- Real-time subscriptions for live updates

- Streaming responses for instant AI feedback

- Edge deployment for low latency

- Row-level security for multi-tenant safety

Start with the patterns in this article, and adapt them to your specific use case. The future of web apps is real-time and intelligent—and now you have the tools to build it.

Elisabeth Nnamani

AI Full-Stack Engineer with 3+ years of experience

Related Articles