Supabase
Supabase BaaS with Postgres, Auth, Realtime, Storage, and Edge Functions
You are an expert in Supabase for rapid backend development, including its Postgres database, row-level security, Auth, Realtime subscriptions, Storage, and Edge Functions. ## Key Points - Always enable RLS on every table — even if you add a permissive policy later, the default-deny stance protects against accidental data leaks. - Use the `service_role` key only in server-side code; never expose it to the browser. - Leverage database functions (`rpc`) for complex logic that should run atomically inside Postgres. - Use Supabase CLI migrations (`supabase db diff`, `supabase migration new`) to version-control schema changes. - Set `cacheControl` on storage uploads and use signed URLs for private buckets. - Use `select()` after `insert`/`update`/`upsert` to return the resulting row in a single round trip. - **Forgetting RLS**: Tables without RLS enabled are publicly readable/writable through the API when using the anon key. - **Using service_role in the browser**: This bypasses RLS entirely and exposes full database access. - **Not handling auth token refresh**: Use `onAuthStateChange` or the Supabase SSR helpers to keep sessions valid. - **Ignoring Postgres connection limits**: Supabase uses connection pooling (PgBouncer) — avoid long-running transactions that hold connections open. - **Large file uploads without chunking**: For files over 6 MB, use `uploadToSignedUrl` with resumable uploads. ## Quick Example ``` NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key> SUPABASE_SERVICE_ROLE_KEY=<service-role-key> # server-side only ``` ```bash # Deploy supabase functions deploy hello ```
skilldb get baas-skills/SupabaseFull skill: 238 linesSupabase — Backend as a Service
You are an expert in Supabase for rapid backend development, including its Postgres database, row-level security, Auth, Realtime subscriptions, Storage, and Edge Functions.
Core Philosophy
Overview
Supabase is an open-source Firebase alternative built on top of PostgreSQL. It provides a full backend stack: a Postgres database with a REST and GraphQL API (via PostgREST), authentication, real-time subscriptions via websockets, file storage, and serverless Edge Functions powered by Deno. Projects can be self-hosted or run on Supabase Cloud.
Setup & Configuration
Creating a Project
# Install the CLI
npm install -g supabase
# Initialize a local project
supabase init
# Link to a remote project
supabase link --project-ref <project-id>
# Start local development stack (Postgres, Auth, Storage, etc.)
supabase start
Client Initialization
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
Environment Variables
NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
SUPABASE_SERVICE_ROLE_KEY=<service-role-key> # server-side only
Core Patterns
Authentication
// 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',
});
// OAuth (e.g., GitHub)
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
});
// Get current session
const { data: { session } } = await supabase.auth.getSession();
// Listen to auth state changes
supabase.auth.onAuthStateChange((event, session) => {
console.log(event, session);
});
Database Queries
// Select with filters
const { data, error } = await supabase
.from('posts')
.select('id, title, author:profiles(name)')
.eq('published', true)
.order('created_at', { ascending: false })
.limit(10);
// Insert
const { data, error } = await supabase
.from('posts')
.insert({ title: 'New Post', body: 'Content', user_id: userId })
.select()
.single();
// Update
const { data, error } = await supabase
.from('posts')
.update({ title: 'Updated Title' })
.eq('id', postId)
.select()
.single();
// Delete
const { error } = await supabase
.from('posts')
.delete()
.eq('id', postId);
// RPC — call a Postgres function
const { data, error } = await supabase.rpc('search_posts', {
query_text: 'supabase',
});
Row-Level Security (RLS)
-- Enable RLS on a table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Users can read published posts
CREATE POLICY "Public read" ON posts
FOR SELECT USING (published = true);
-- Users can insert their own posts
CREATE POLICY "Insert own" ON posts
FOR INSERT WITH CHECK (auth.uid() = user_id);
-- Users can update their own posts
CREATE POLICY "Update own" ON posts
FOR UPDATE USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Users can delete their own posts
CREATE POLICY "Delete own" ON posts
FOR DELETE USING (auth.uid() = user_id);
Realtime Subscriptions
const channel = supabase
.channel('posts-changes')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => {
console.log('Change received:', payload);
}
)
.subscribe();
// Cleanup
supabase.removeChannel(channel);
Storage
// Upload a file
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${userId}/avatar.png`, file, {
cacheControl: '3600',
upsert: true,
});
// Get a public URL
const { data } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.png`);
// Download a file
const { data, error } = await supabase.storage
.from('avatars')
.download(`${userId}/avatar.png`);
Edge Functions
// supabase/functions/hello/index.ts
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
serve(async (req) => {
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
const { name } = await req.json();
return new Response(JSON.stringify({ message: `Hello ${name}` }), {
headers: { 'Content-Type': 'application/json' },
});
});
# Deploy
supabase functions deploy hello
Best Practices
- Always enable RLS on every table — even if you add a permissive policy later, the default-deny stance protects against accidental data leaks.
- Use the
service_rolekey only in server-side code; never expose it to the browser. - Leverage database functions (
rpc) for complex logic that should run atomically inside Postgres. - Use Supabase CLI migrations (
supabase db diff,supabase migration new) to version-control schema changes. - Set
cacheControlon storage uploads and use signed URLs for private buckets. - Use
select()afterinsert/update/upsertto return the resulting row in a single round trip.
Common Pitfalls
- Forgetting RLS: Tables without RLS enabled are publicly readable/writable through the API when using the anon key.
- Using service_role in the browser: This bypasses RLS entirely and exposes full database access.
- Not handling auth token refresh: Use
onAuthStateChangeor the Supabase SSR helpers to keep sessions valid. - Ignoring Postgres connection limits: Supabase uses connection pooling (PgBouncer) — avoid long-running transactions that hold connections open.
- Large file uploads without chunking: For files over 6 MB, use
uploadToSignedUrlwith resumable uploads.
Anti-Patterns
Over-engineering for hypothetical requirements. Building for scenarios that may never materialize adds complexity without value. Solve the problem in front of you first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide wastes time and introduces risk.
Premature abstraction. Creating elaborate frameworks before having enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at system boundaries. Internal code can trust its inputs, but boundaries with external systems require defensive validation.
Skipping documentation. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add baas-skills
Related Skills
Appwrite
Appwrite self-hosted BaaS with database, auth, storage, and serverless functions
AWS Amplify
AWS Amplify BaaS with AppSync GraphQL, Cognito auth, S3 storage, and Lambda functions
Backendless
Backendless BaaS with real-time database, user authentication, Cloud Code,
Clerk Auth
Clerk authentication service with pre-built UI components, session management, and multi-framework support
Convex
Convex real-time backend with reactive queries, mutations, and serverless functions
Encore
Encore is a backend development platform that automatically provisions, configures, and manages cloud infrastructure based on your Go code. It simplifies building and deploying cloud-native applications by allowing you to focus purely on business logic.