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
┌─────────────────────────────────────────────────────────┐
│ 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:
// 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:
// 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:
// 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:
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

From CRUD to Cognition: Building AI-Native Web Applications
Most web apps just store and display data. AI-native apps reason over it. This article breaks down how I design full-stack applications that combine traditional systems with LLMs—covering architecture, RAG pipelines, and how to turn static dashboards into intelligent, queryable systems.

Turning Business Workflows into Automated Systems with AI
Manual workflows kill productivity. In this article, I show how I design and build internal tools and automation systems that replace repetitive tasks using AI—covering everything from input pipelines to decision-making outputs that actually save time and money.