Supabase provides a PostgreSQL database, authentication, and real-time subscriptions as a managed backend โ pairing perfectly with Next.js for building full-stack real-time apps quickly. This guide builds a real-time app from database to deployment.
๐ Table of Contents
Why Next.js + Supabase?
- Full backend without building one: Database, auth, storage, and real-time out of the box
- Real PostgreSQL: Not a proprietary database โ standard SQL you can query directly
- Real-time subscriptions: Listen to database changes live with minimal code
- Built-in auth: Email, OAuth, magic links โ no auth infrastructure to build
- Open source: Self-hostable, avoiding vendor lock-in
Setup
npx create-next-app@latest my-app
cd my-app
npm install @supabase/supabase-js @supabase/ssr
# Create a project at supabase.com, then add to .env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
Supabase Client Setup
// lib/supabase.js
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
Create a Table (SQL Editor in Supabase)
-- Create a messages table
create table messages (
id uuid default gen_random_uuid() primary key,
content text not null,
user_id uuid references auth.users,
created_at timestamptz default now()
);
-- Enable Row Level Security
alter table messages enable row level security;
-- Policy: anyone can read messages
create policy "Messages are viewable by everyone"
on messages for select using (true);
-- Policy: authenticated users can insert their own messages
create policy "Users can insert their own messages"
on messages for insert
with check (auth.uid() = user_id);
Real-Time Subscriptions
// components/Chat.jsx
'use client';
import { useState, useEffect } from 'react';
import { supabase } from '@/lib/supabase';
export default function Chat() {
const [messages, setMessages] = useState([]);
useEffect(() => {
// Load existing messages
supabase.from('messages').select('*').order('created_at')
.then(({ data }) => setMessages(data || []));
// Subscribe to new messages in real time
const channel = supabase
.channel('messages')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => {
setMessages(prev => [...prev, payload.new]);
}
)
.subscribe();
// Cleanup subscription
return () => { supabase.removeChannel(channel); };
}, []);
const sendMessage = async (content) => {
const { data: { user } } = await supabase.auth.getUser();
await supabase.from('messages').insert({ content, user_id: user.id });
};
return (
<div>
{messages.map(m => <div key={m.id}>{m.content}</div>)}
</div>
);
}
Authentication
// Sign up
async function signUp(email, password) {
const { data, error } = await supabase.auth.signUp({ email, password });
if (error) throw error;
return data;
}
// Sign in
async function signIn(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({ email, password });
if (error) throw error;
return data;
}
// OAuth (Google, GitHub, etc.)
async function signInWithGitHub() {
await supabase.auth.signInWithOAuth({ provider: 'github' });
}
// Get current user
const { data: { user } } = await supabase.auth.getUser();
// Sign out
await supabase.auth.signOut();
Server Components and Data Fetching
// app/page.jsx - Server Component fetching data
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export default async function Page() {
const cookieStore = cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{ cookies: { get: (name) => cookieStore.get(name)?.value } }
);
const { data: messages } = await supabase
.from('messages')
.select('*')
.order('created_at', { ascending: false })
.limit(20);
return <MessageList initialMessages={messages} />;
}
Row Level Security: The Key to Safe Data
Supabase uses PostgreSQL Row Level Security (RLS) to control data access. Since the client connects directly with the anon key, RLS policies are what keep data safe โ they define who can read/write what. Always enable RLS on your tables and write policies. Without RLS, your data is exposed. RLS is not optional for production โ it’s the security foundation of the direct-client model.
Deployment
# Deploy to Vercel (seamless with Next.js)
# 1. Push to GitHub
# 2. Import the repo in Vercel
# 3. Add environment variables (NEXT_PUBLIC_SUPABASE_URL, ANON_KEY)
# 4. Deploy
# Supabase is already hosted - no backend deployment needed.
# Your Next.js frontend on Vercel + Supabase backend = full-stack app live.
Frequently Asked Questions
Q: Is Supabase a good alternative to building my own backend?
A: Yes, for many apps โ it provides a real PostgreSQL database, auth, storage, and real-time without building backend infrastructure. You get standard SQL (not a proprietary database), so you’re not locked in. It’s excellent for rapid full-stack development and MVPs.
Q: How does real-time work in Supabase?
A: Supabase listens to PostgreSQL changes and pushes them to subscribed clients over WebSockets. You subscribe to INSERT/UPDATE/DELETE events on a table, and your callback fires when data changes. Minimal code gives you live updates โ great for chat, notifications, and collaborative features.
Q: Is it safe to use Supabase directly from the client?
A: Yes, IF you use Row Level Security (RLS). Since the client connects with the anon key, RLS policies control what each user can read/write. Always enable RLS and write policies โ it’s the security foundation. Without RLS, your data is exposed. Never skip it in production.
Q: Supabase or Firebase?
A: Supabase uses PostgreSQL (standard SQL, relational, no lock-in) and is open source/self-hostable. Firebase uses a proprietary NoSQL database. Choose Supabase if you prefer SQL, relational data, and avoiding lock-in; Firebase for its mature ecosystem and if NoSQL fits your data. Both offer auth and real-time.
Q: Can I self-host Supabase?
A: Yes โ Supabase is open source and can be self-hosted, avoiding vendor lock-in. Most start with the managed cloud (generous free tier) and could self-host later if needed. The self-hosting option is a key advantage over closed alternatives.
Conclusion
Next.js + Supabase is a powerful, fast way to build full-stack real-time apps in 2026. Supabase provides a real PostgreSQL database, built-in authentication, and real-time subscriptions โ pairing seamlessly with Next.js. The workflow: create tables with SQL, enable Row Level Security with policies (critical for safe direct-client access), subscribe to real-time changes, and use Supabase auth for users. Deploy the Next.js frontend to Vercel, and since Supabase is already hosted, you have a full-stack app live with no backend infrastructure to manage. The key security rule is always enabling RLS โ it’s what keeps your directly-accessed data safe. For chat, collaborative tools, dashboards, and MVPs, this stack lets you build real-time full-stack apps remarkably quickly without building or managing a backend.
๐ You might also like
๐ Share this article




โ๏ธ Leave a Comment