
How To Build A Full-Stack App With Next.js, Supabase & AI Integration
What You’ll Build
A simple Next.js app with:
-
Supabase for authentication, database, and real-time updates
-
AI integration (e.g., OpenAI GPT API) for adding AI-powered features like chat, content generation, or smart suggestions
Prerequisites
-
Node.js installed (v16+ recommended)
-
Basic knowledge of React and Next.js
-
Supabase account (free tier available)
-
OpenAI account or another AI provider API key (optional for AI features)
Step 1: Set Up Next.js Project
-
Create a new Next.js app:
npx create-next-app@latest my-ai-app cd my-ai-app
-
Start the dev server:
npm run dev
Open http://localhost:3000
to check it’s running.
Step 2: Set Up Supabase
-
Go to Supabase and create a free project.
-
Get your API URL and anon public API key from the project dashboard.
-
Set up your database schema using Supabase SQL editor or table editor (e.g., create a
messages
table for chat orusers
table).
Example SQL to create a simple messages
table:
create table messages ( id bigint generated by default as identity primary key, user_id uuid references auth.users not null, content text not null, created_at timestamp with time zone default timezone('utc'::text, now()) not null );
Step 3: Install Supabase Client in Next.js
npm install @supabase/supabase-js
Create a file lib/supabaseClient.js
to initialize Supabase client:
import { createClient } from '@supabase/supabase-js'; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; export const supabase = createClient(supabaseUrl, supabaseAnonKey);
Add these environment variables to .env.local
:
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
Step 4: Implement Authentication with Supabase
Create a simple auth page (pages/auth.js
) using Supabase Auth UI or custom components.
Example for email signup/login:
import { useState } from 'react'; import { supabase } from '../lib/supabaseClient'; export default function Auth() { const [email, setEmail] = useState(''); const [message, setMessage] = useState(''); const handleLogin = async () => { const { error } = await supabase.auth.signInWithOtp({ email }); if (error) setMessage(error.message); else setMessage('Check your email for the login link!'); }; return ( <div> <h1>Login / Signup</h1> <input type="email" placeholder="your@email.com" value={email} onChange={(e) => setEmail(e.target.value)} /> <button onClick={handleLogin}>Send Magic Link</button> <p>{message}</p> </div> ); }
Step 5: Create a Basic Chat or Content Page
Example: A page where logged-in users can submit messages and see messages from others (stored in Supabase).
pages/index.js
:
import { useEffect, useState } from 'react'; import { supabase } from '../lib/supabaseClient'; export default function Home() { const [session, setSession] = useState(null); const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); useEffect(() => { setSession(supabase.auth.getSession().then(({ data }) => setSession(data.session))); const subscription = supabase .channel('public:messages') .on('postgres_changes', { event: '*', schema: 'public', table: 'messages' }, payload => { setMessages((messages) => [...messages, payload.new]); }) .subscribe(); fetchMessages(); return () => { supabase.removeChannel(subscription); }; }, []); async function fetchMessages() { let { data } = await supabase.from('messages').select('*').order('created_at', { ascending: true }); setMessages(data); } async function sendMessage() { if (!input) return; await supabase.from('messages').insert([{ content: input, user_id: session.user.id }]); setInput(''); } if (!session) return <p>Please log in</p>; return ( <div> <h1>Chat Room</h1> <div style={{ maxHeight: '300px', overflowY: 'auto' }}> {messages.map((msg) => ( <p key={msg.id}>{msg.content}</p> ))} </div> <input type="text" value={input} onChange={(e) => setInput(e.target.value)} placeholder="Type a message" /> <button onClick={sendMessage}>Send</button> </div> ); }
Step 6: Integrate AI (e.g., OpenAI GPT)
-
Install OpenAI client:
npm install openai
-
Create an API route for AI requests:
pages/api/ai.js
import { Configuration, OpenAIApi } from 'openai'; const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY, }); const openai = new OpenAIApi(configuration); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: 'Only POST requests allowed' }); } const { prompt } = req.body; try { const completion = await openai.createChatCompletion({ model: 'gpt-4', messages: [{ role: 'user', content: prompt }], }); res.status(200).json({ result: completion.data.choices[0].message.content }); } catch (error) { res.status(500).json({ error: error.message || 'Error with AI API' }); } }
-
Add the API key to
.env.local
:
OPENAI_API_KEY=your-openai-api-key
Step 7: Call AI API from Frontend
Update the chat app to send user input to AI and display the response.
async function sendMessage() { if (!input) return; // Save user message await supabase.from('messages').insert([{ content: input, user_id: session.user.id }]); // Call AI API const response = await fetch('/api/ai', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: input }), }); const data = await response.json(); // Save AI response await supabase.from('messages').insert([{ content: data.result, user_id: 'AI' }]); setInput(''); }
Step 8: Deploy Your App
-
Push your code to GitHub.
-
Connect your repository to Vercel for seamless Next.js deployment.
-
Add your environment variables (Supabase URL, anon key, OpenAI API key) in Vercel dashboard.
Bonus Tips
-
Use Supabase Edge Functions for serverless AI calls to protect API keys.
-
Add middleware in Next.js for protected routes.
-
Use Tailwind CSS or UI libraries to style your app faster.
-
Enable real-time subscriptions for live chat experiences.
-
Explore other AI models or tools for image generation, speech, or recommendations.
Summary
Step | Key Tools / Concepts |
---|---|
1. Setup Next.js | create-next-app |
2. Setup Supabase | Database, Auth, Real-time DB |
3. Connect Supabase Client | @supabase/supabase-js |
4. Implement Auth | Supabase Auth with Magic Link |
5. Build Chat Interface | Realtime subscriptions, React state |
6. AI Integration | OpenAI API, Next.js API routes |
7. Deploy | Vercel or other hosting |
Certainly! Below is a comprehensive, detailed 2000-word case study guide on How to Build a Full-Stack App with Next.js, Supabase & AI Integration, including practical examples, explanations, and real-world workflows.
Overview of the Tech Stack
-
Next.js: React framework optimized for SEO, server-side rendering, API routes, and static generation.
-
Supabase: Provides PostgreSQL database, real-time subscriptions, authentication, and storage out of the box.
-
AI Integration: Using OpenAI GPT APIs (or similar) for natural language processing, code generation, or chatbots.
The Case Study: AI-Powered Chat Application with User Authentication and Real-Time Messaging
Project Goals
-
Allow users to sign up and log in with email magic links (via Supabase Auth).
-
Enable authenticated users to chat with an AI assistant powered by OpenAI GPT.
-
Store all chat messages in Supabase’s real-time PostgreSQL database.
-
Use Next.js API routes as a middle layer to securely proxy requests to the OpenAI API.
-
Provide a clean, responsive UI with real-time message updates.
Part 1: Setting Up the Development Environment
Step 1: Initialize Next.js Project
npx create-next-app ai-chat-app cd ai-chat-app npm run dev
This bootstraps the frontend with React and Next.js features.
Step 2: Create Supabase Project
-
Go to Supabase and create a free project.
-
Note your Supabase URL and Anon Key (found under Project Settings).
-
In Supabase’s SQL editor, create a
messages
table:
create table messages ( id bigint generated by default as identity primary key, user_id uuid references auth.users not null, content text not null, is_ai boolean default false, created_at timestamp with time zone default timezone('utc'::text, now()) not null );
Step 3: Install Dependencies
npm install @supabase/supabase-js openai
-
@supabase/supabase-js
to interact with Supabase. -
openai
for AI integration.
Step 4: Configure Environment Variables
Create .env.local
with:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key OPENAI_API_KEY=your_openai_api_key
Never expose OPENAI_API_KEY
on the client side; use API routes for secure calls.
Part 2: Authentication with Supabase
Example: Magic Link Email Auth
Create a file lib/supabaseClient.js
:
import { createClient } from '@supabase/supabase-js'; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; export const supabase = createClient(supabaseUrl, supabaseAnonKey);
Create pages/auth.js
:
import { useState } from 'react'; import { supabase } from '../lib/supabaseClient'; export default function Auth() { const [email, setEmail] = useState(''); const [message, setMessage] = useState(''); async function handleLogin() { const { error } = await supabase.auth.signInWithOtp({ email }); if (error) setMessage(error.message); else setMessage('Check your email for a magic link!'); } return ( <div> <h1>Login or Sign Up</h1> <input type="email" placeholder="you@example.com" onChange={(e) => setEmail(e.target.value)} value={email} /> <button onClick={handleLogin}>Send Magic Link</button> <p>{message}</p> </div> ); }
Key Learning:
-
Supabase handles sending magic link emails, authentication state, and user session management seamlessly.
-
No backend code needed for user management.
Part 3: Building the Chat Interface with Next.js & Supabase
Real-Time Message Fetching
Using Supabase’s realtime subscriptions to update the chat UI instantly when new messages arrive.
pages/index.js
:
import { useState, useEffect } from 'react'; import { supabase } from '../lib/supabaseClient'; export default function Chat() { const [session, setSession] = useState(null); const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); useEffect(() => { supabase.auth.getSession().then(({ data }) => setSession(data.session)); fetchMessages(); const subscription = supabase .channel('public:messages') .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) => { setMessages((messages) => [...messages, payload.new]); }) .subscribe(); return () => { supabase.removeChannel(subscription); }; }, []); async function fetchMessages() { let { data } = await supabase.from('messages').select('*').order('created_at', { ascending: true }); setMessages(data); } async function sendMessage() { if (!input.trim() || !session) return; await supabase.from('messages').insert([ { content: input, user_id: session.user.id, is_ai: false }, ]); setInput(''); } if (!session) return <p>Please log in to chat.</p>; return ( <div> <div style={{ height: '300px', overflowY: 'auto' }}> {messages.map((msg) => ( <p key={msg.id} style={{ color: msg.is_ai ? 'blue' : 'black' }}> {msg.content} </p> ))} </div> <input type="text" placeholder="Type your message..." value={input} onChange={(e) => setInput(e.target.value)} /> <button onClick={sendMessage}>Send</button> </div> ); }
Part 4: AI Integration with OpenAI API
Creating a Secure API Route in Next.js
Create pages/api/chat.js
:
import { Configuration, OpenAIApi } from 'openai'; const config = new Configuration({ apiKey: process.env.OPENAI_API_KEY, }); const openai = new OpenAIApi(config); export default async function handler(req, res) { if (req.method !== 'POST') return res.status(405).json({ error: 'Method not allowed' }); const { messages } = req.body; if (!messages) return res.status(400).json({ error: 'No messages provided' }); try { const completion = await openai.createChatCompletion({ model: 'gpt-4', messages, }); const aiMessage = completion.data.choices[0].message; res.status(200).json({ message: aiMessage }); } catch (error) { res.status(500).json({ error: error.message }); } }
How to Use This API from the Frontend
Modify sendMessage
in pages/index.js
:
async function sendMessage() { if (!input.trim() || !session) return; // Insert user message await supabase.from('messages').insert([ { content: input, user_id: session.user.id, is_ai: false }, ]); // Prepare chat history for context (simplified) const chatHistory = messages.map(msg => ({ role: msg.is_ai ? 'assistant' : 'user', content: msg.content, })); // Add latest user message to history chatHistory.push({ role: 'user', content: input }); // Call AI API const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: chatHistory }), }); const data = await response.json(); if (data.message) { // Save AI response to DB await supabase.from('messages').insert([ { content: data.message.content, user_id: null, is_ai: true }, ]); } setInput(''); }
Part 5: Real-World Challenges & Solutions
Challenge 1: Maintaining Chat Context
-
Problem: Passing entire chat history to OpenAI every time can grow large.
-
Solution: Limit the message history to recent N messages or summarize previous conversations.
Challenge 2: Handling Authentication State
-
Keep session state updated with Supabase hooks or listeners.
-
Redirect unauthenticated users to
/auth
.
Challenge 3: Securing API Keys
-
Use Next.js API routes to prevent exposing OpenAI keys.
-
Never call OpenAI directly from frontend.
Challenge 4: Realtime Sync Lag or Missed Events
-
Use Supabase’s real-time channels carefully.
-
Handle reconnection logic and fallback fetches for missed messages.
Part 6: Deployment & Scaling
Deploying to Vercel
-
Push your code to GitHub.
-
Connect your repo to Vercel.
-
Add environment variables on Vercel dashboard.
-
Deploy and test.
Scaling Tips
-
Use Supabase’s edge functions for low latency.
-
Cache AI responses for repeated queries.
-
Monitor API usage and rate limits on OpenAI.
Part 7: Extending Your App
Ideas for Additional Features
-
Add user profiles and avatars with Supabase storage.
-
Integrate payment gateways for premium AI usage.
-
Add multi-language support by detecting user language and translating prompts.
-
Build analytics dashboards using Supabase and Next.js API routes.
-
Use AI for moderation to filter inappropriate messages.
Conclusion
Building a full-stack app with Next.js, Supabase, and AI integration is an achievable project that leverages modern tools to deliver powerful user experiences. This stack supports fast development, scalability, and real-time interactivity with minimal backend overhead.
You’ve seen how to:
-
Set up authentication and real-time database with Supabase.
-
Build a responsive chat UI with Next.js.
-
Securely integrate AI APIs using Next.js API routes.
-
Handle common challenges and optimize the app.