Supabase Complete Guide | Auth, Database, Storage, Realtime
이 글의 핵심
Supabase gives you a full backend — Postgres database, authentication, file storage, and realtime subscriptions — without managing infrastructure. This guide covers all four pillars with practical examples.
What This Guide Covers
Supabase provides authentication, a managed PostgreSQL database, file storage, and realtime subscriptions — all accessible via a simple JavaScript SDK. This guide covers each feature with practical examples.
Why Supabase?
Supabase has emerged as the leading open-source Firebase alternative, with over 50,000 projects on their hosted platform and thousands of self-hosted deployments.
Real-world adoption:
- GitHub uses Supabase for internal tools
- Mobbin (design inspiration platform, 1M+ users) built entirely on Supabase
- Vercel templates include Supabase as the default backend option
- Thousands of startups ship MVPs in days instead of weeks
Why teams choose Supabase over building custom backends:
- Launch in hours, not weeks — authentication, database, and file storage work out of the box
- PostgreSQL, not NoSQL — use SQL, joins, transactions, and your existing database knowledge
- Row Level Security — built-in authorization at the database level (not application layer)
- Realtime by default — subscribe to database changes without WebSocket boilerplate
- Open source — self-host if needed, no vendor lock-in
Supabase vs Firebase:
| Feature | Supabase | Firebase |
|---|---|---|
| Database | PostgreSQL (SQL) | Firestore (NoSQL) |
| Auth | Open source (GoTrue) | Proprietary |
| Self-hosting | Yes | No |
| Querying | SQL, joins, full-text search | Document queries, limited joins |
| Pricing | Free tier: 500MB, 2GB bandwidth | Free tier: 1GB storage, 10GB bandwidth |
When to use Supabase:
- You prefer SQL and relational data modeling
- Need complex queries, joins, or full-text search
- Want to self-host or avoid vendor lock-in
- Building web apps (Next.js, Remix, SvelteKit)
When to use Firebase:
- Need the most mature mobile SDK (Flutter, iOS, Android)
- Require Google ecosystem integration (Firebase ML, Analytics)
- Prefer NoSQL document structure
- Need advanced real-time features (Firestore’s nested listeners)
Setup
npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
Get your URL and anon key from the Supabase dashboard under Project Settings → API.
1. Authentication
Email & Password
// Sign up
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'secure-password',
});
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'secure-password',
});
// Sign out
await supabase.auth.signOut();
// Get current user
const { data: { user } } = await supabase.auth.getUser();
OAuth (Google, GitHub, etc.)
await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'http://localhost:3000/auth/callback',
},
});
Auth State Listener
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
if (event === 'SIGNED_IN') console.log('Signed in:', session.user.email);
if (event === 'SIGNED_OUT') console.log('Signed out');
}
);
// Cleanup
subscription.unsubscribe();
2. Database (PostgreSQL)
Supabase exposes your Postgres database via a REST API. The JavaScript client provides a fluent query builder.
Basic CRUD
// SELECT
const { data, error } = await supabase
.from('posts')
.select('id, title, created_at')
.order('created_at', { ascending: false })
.limit(10);
// INSERT
const { data, error } = await supabase
.from('posts')
.insert({ title: 'Hello World', body: 'My first post', user_id: user.id })
.select()
.single();
// UPDATE
const { error } = await supabase
.from('posts')
.update({ title: 'Updated Title' })
.eq('id', postId);
// DELETE
const { error } = await supabase
.from('posts')
.delete()
.eq('id', postId);
Filtering
// Equality
.eq('status', 'published')
// Range
.gte('views', 100).lte('views', 1000)
// Pattern matching
.ilike('title', '%supabase%')
// In array
.in('category', ['tech', 'ai', 'web'])
// Is null
.is('deleted_at', null)
Joins
const { data } = await supabase
.from('posts')
.select(`
id,
title,
author:profiles(id, username, avatar_url),
comments(id, body)
`);
3. Row Level Security (RLS)
RLS is Supabase’s most important security feature. Enable it on every table, then write policies:
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Anyone can read published posts
CREATE POLICY "Public posts are visible to all"
ON posts FOR SELECT
USING (status = 'published');
-- Users can only edit their own posts
CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
USING (auth.uid() = user_id);
-- Users can only insert as themselves
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
With RLS enabled, your anon key is safe to use in client-side code — database policies enforce access control.
4. Realtime Subscriptions
Subscribe to database changes in real time:
// Subscribe to all changes on a table
const channel = supabase
.channel('posts-changes')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => {
if (payload.eventType === 'INSERT') {
setPosts(prev => [payload.new, ...prev]);
}
if (payload.eventType === 'UPDATE') {
setPosts(prev => prev.map(p => p.id === payload.new.id ? payload.new : p));
}
if (payload.eventType === 'DELETE') {
setPosts(prev => prev.filter(p => p.id !== payload.old.id));
}
}
)
.subscribe();
// Cleanup
supabase.removeChannel(channel);
Broadcast (custom events)
Send events between clients without touching the database:
const channel = supabase.channel('room-1');
// Listen
channel.on('broadcast', { event: 'cursor' }, ({ payload }) => {
updateCursor(payload.userId, payload.x, payload.y);
}).subscribe();
// Send
channel.send({
type: 'broadcast',
event: 'cursor',
payload: { userId: user.id, x: 100, y: 200 },
});
5. Storage
// Upload a file
const { data, error } = await supabase.storage
.from('avatars')
.upload(`public/${user.id}.jpg`, file, {
cacheControl: '3600',
upsert: true,
});
// Get public URL
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`public/${user.id}.jpg`);
// Download
const { data, error } = await supabase.storage
.from('avatars')
.download(`public/${user.id}.jpg`);
// Delete
const { error } = await supabase.storage
.from('avatars')
.remove([`public/${user.id}.jpg`]);
6. Edge Functions
Supabase Edge Functions run TypeScript/JavaScript at the edge (Deno runtime):
// supabase/functions/send-email/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
serve(async (req) => {
const { to, subject, body } = await req.json();
// Call external email API...
return new Response(JSON.stringify({ sent: true }), {
headers: { "Content-Type": "application/json" },
});
});
supabase functions deploy send-email
Call from client:
const { data, error } = await supabase.functions.invoke('send-email', {
body: { to: 'user@example.com', subject: 'Hello', body: 'World' }
});
Next.js App Router Integration
npm install @supabase/ssr
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export function createClient() {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name) { return cookieStore.get(name)?.value; },
set(name, value, options) { cookieStore.set({ name, value, ...options }); },
remove(name, options) { cookieStore.set({ name, value: '', ...options }); },
},
}
);
}
// app/dashboard/page.tsx
export default async function Dashboard() {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect('/login');
const { data: posts } = await supabase.from('posts').select('*');
return <PostList posts={posts} />;
}
Key Takeaways
| Feature | Use for |
|---|---|
| Auth | Email/password, OAuth, magic links |
| Database | Postgres with REST API + type-safe client |
| RLS | Row-level access control per user |
| Realtime | Live database changes + broadcast events |
| Storage | File uploads with access control |
| Edge Functions | Server-side logic, webhooks, integrations |
Enable RLS on every table from day one — it’s much harder to add later. Start with the anon key for public data and the service_role key only in secure server-side contexts.
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Build full-stack apps with Supabase — the open-source Firebase alternative.
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
Keywords Covered in This Article (Related Search Terms)
This article covers Supabase, PostgreSQL, Auth, Realtime, Backend, Firebase.