The Complete Supabase Guide | Firebase Alternative, PostgreSQL, Auth, Storage, Realtime, Edge Functions
What this post covers
This is a complete guide to building full-stack applications with Supabase. It covers PostgreSQL, Auth, Storage, Realtime, and Edge Functions as practical examples of an open-source Firebase alternative.
From the field: After migrating from Firebase to Supabase, we gained more database flexibility and cut costs by about 60%.
Introduction: “Firebase is expensive”
Real-world scenarios
Scenario 1: I need complex queries
Firestore is limited. Supabase uses PostgreSQL. Scenario 2: Costs are too high
Firebase can be costly. Supabase is open source and more affordable. Scenario 3: I’m worried about vendor lock-in
Firebase ties you to its ecosystem. Supabase uses standard PostgreSQL.
1. What is Supabase?
Core characteristics
Supabase is an open-source Firebase alternative. Key features:
- PostgreSQL: a powerful relational database
- Auth: email, OAuth, Magic Link
- Storage: file uploads
- Realtime: live subscriptions
- Edge Functions: Deno-based serverless
- Row Level Security: access control
2. Project setup
Install
npm install @supabase/supabase-js
Initialize
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
export const supabase = createClient(supabaseUrl, supabaseKey);
3. Database (PostgreSQL)
Creating tables
Below is a detailed implementation using SQL. Read through the code while understanding the role of each part.
-- Run in SQL Editor
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
content TEXT,
author_id UUID REFERENCES users(id),
published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
CRUD
Below is a detailed implementation using TypeScript. It uses async flows for efficiency and error handling for reliability. Read through the code while understanding the role of each part.
// Create
const { data, error } = await supabase
.from('users')
.insert({ email: '[email protected]', name: 'John' })
.select()
.single();
// Read
const { data: users } = await supabase
.from('users')
.select('*')
.order('created_at', { ascending: false });
// Read with Join
const { data: posts } = await supabase
.from('posts')
.select(`
*,
author:users(name, email)
`)
.eq('published', true);
// Update
const { data } = await supabase
.from('users')
.update({ name: 'John Updated' })
.eq('id', userId)
.select();
// Delete
const { error } = await supabase
.from('users')
.delete()
.eq('id', userId);
4. Authentication (Auth)
Email sign-up
Below is a simple TypeScript example. It uses async flows for efficiency and error handling for reliability. Run the code yourself to see how it behaves.
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'password123',
});
Sign in
Below is a simple TypeScript example. It uses async flows for efficiency and error handling for reliability. Run the code yourself to see how it behaves.
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'password123',
});
OAuth
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
});
Session management
The code below is a TypeScript example. It uses async flows for efficiency. Read through the code while understanding the role of each part.
// Current user
const { data: { user } } = await supabase.auth.getUser();
// Sign out
await supabase.auth.signOut();
// Listen for session changes
supabase.auth.onAuthStateChange((event, session) => {
console.log('Auth event:', event, session);
});
5. Storage
File upload
The code below is a TypeScript example. It uses async flows for efficiency and error handling for reliability. Read through the code while understanding the role of each part.
const file = event.target.files[0];
const { data, error } = await supabase.storage
.from('avatars')
.upload(`public/${userId}/${file.name}`, file);
// Public URL
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`public/${userId}/${file.name}`);
File download
const { data, error } = await supabase.storage
.from('avatars')
.download(`public/${userId}/avatar.png`);
6. Realtime
Subscriptions
Below is a detailed TypeScript implementation. Read through the code while understanding the role of each part.
const channel = supabase
.channel('posts')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'posts',
},
(payload) => {
console.log('Change:', payload);
}
)
.subscribe();
// Unsubscribe
channel.unsubscribe();
React example
Below is a detailed TypeScript implementation. It imports the modules you need, uses async flows for efficiency, and maps over data in the render tree. Read through the code while understanding the role of each part.
import { useEffect, useState } from 'react';
import { supabase } from './lib/supabase';
export default function Posts() {
const [posts, setPosts] = useState([]);
useEffect(() => {
// Initial data
fetchPosts();
// Realtime subscription
const channel = supabase
.channel('posts')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
() => {
fetchPosts();
}
)
.subscribe();
return () => {
channel.unsubscribe();
};
}, []);
async function fetchPosts() {
const { data } = await supabase.from('posts').select('*');
setPosts(data || []);
}
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
7. Row Level Security (RLS)
Policy setup
Below is a detailed implementation using SQL. Read through the code while understanding the role of each part.
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Read: everyone can view
CREATE POLICY "Public posts are viewable by everyone"
ON posts FOR SELECT
USING (published = true);
-- Write: only the author
CREATE POLICY "Users can create their own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = author_id);
CREATE POLICY "Users can update their own posts"
ON posts FOR UPDATE
USING (auth.uid() = author_id);
8. Edge Functions
Create a function
npx supabase functions new hello
The code below is a TypeScript example. It imports the modules you need and uses async flows for efficiency. Read through the code while understanding the role of each part.
// supabase/functions/hello/index.ts
import { serve } from 'https://deno.land/[email protected]/http/server.ts';
serve(async (req) => {
const { name } = await req.json();
return new Response(
JSON.stringify({ message: `Hello ${name}!` }),
{ headers: { 'Content-Type': 'application/json' } }
);
});
Deploy
npx supabase functions deploy hello
Invoke
const { data, error } = await supabase.functions.invoke('hello', {
body: { name: 'John' },
});
Connecting to job search and interviews
If you can explain Auth, RLS, and Edge Functions as a full-stack portfolio story, it carries a lot of weight in interviews. Pair Tech Interview Preparation Guide with the achievements and tech-stack sections in Developer Job Hunting Practical Tips when you describe your projects.
Summary and checklist
Key takeaways
- Supabase: open-source Firebase alternative
- PostgreSQL: powerful relational database
- Auth: multiple authentication options
- Storage: file management
- Realtime: live subscriptions
- RLS: security policies
Implementation checklist
- Create a project
- Design tables
- Implement CRUD
- Configure Auth
- Implement Storage
- Add Realtime subscriptions
- Configure RLS policies
Related reading
Keywords in this post
Supabase, Firebase, PostgreSQL, Auth, Storage, Realtime, Backend
Frequently asked questions (FAQ)
Q. How does it compare to Firebase?
A. Supabase is built on PostgreSQL, so you can run complex queries and often keep costs lower. Firebase is simpler to start with but more limited for relational data and advanced queries.
Q. Is there a free tier?
A. Yes. The free plan includes up to 500MB database, 1GB Storage, and 50K MAU.
Q. Can I self-host?
A. Yes. It is open source and you can self-host with Docker Compose.
Q. Is it production-ready?
A. Yes. Many startups and enterprises use it reliably in production.