Overview
This quickstart guide will walk you through:
Creating a Supabase project
Setting up a database table
Connecting from your application
Performing CRUD operations
Adding authentication
Securing data with Row Level Security
By the end, you’ll have a working application with authentication and a secure database.
Step 1: Create a Supabase Project
Sign up for Supabase
Navigate to supabase.com/dashboard and sign up for a free account using:
GitHub
GitLab
Bitbucket
Email/password
Using a Git provider enables seamless integration with CI/CD pipelines and Vercel deployments.
Create a new project
Click New Project and fill in the details:
Name : Choose a descriptive name (e.g., “my-app”)
Database Password : Use a strong password and save it securely
Region : Select the region closest to your users
Pricing Plan : Start with the Free tier
Click Create new project and wait 2-3 minutes for provisioning. Save your database password! You’ll need it for direct database connections. The password cannot be recovered, only reset.
Get your API credentials
Once your project is ready:
Go to Project Settings (gear icon in sidebar)
Navigate to the API section
Copy the following values:
Project URL (e.g., https://xyzcompany.supabase.co)
anon public key (safe to use in browsers)
You’ll use these to connect your application.
Step 2: Create Your First Table
Let’s create a simple todos table to manage tasks.
Open SQL Editor
Navigate to SQL Editor in the left sidebar. This is where you can run SQL queries and manage your database schema.
Create the table
Click New Query and paste the following SQL: -- Create todos table
create table todos (
id bigint generated always as identity primary key ,
user_id uuid references auth . users (id) on delete cascade not null ,
title text not null ,
description text ,
is_complete boolean default false,
created_at timestamptz default now ()
);
-- Enable Row Level Security
alter table todos enable row level security ;
-- Create policies
create policy "Users can view their own todos"
on todos for select
using ( auth . uid () = user_id);
create policy "Users can create their own todos"
on todos for insert
with check ( auth . uid () = user_id);
create policy "Users can update their own todos"
on todos for update
using ( auth . uid () = user_id);
create policy "Users can delete their own todos"
on todos for delete
using ( auth . uid () = user_id);
Click Run or press Cmd/Ctrl + Enter to execute.
Verify the table
Navigate to Table Editor in the sidebar. You should see your new todos table with the defined columns. You can manually add test data here, but we’ll insert data programmatically in the next step.
What is Row Level Security (RLS)? RLS is a PostgreSQL feature that restricts which rows users can access. The policies we created ensure users can only see and modify their own todos. This security is enforced at the database level, not in your application code.
Step 3: Install the Supabase Client
Now let’s connect to Supabase from your application.
JavaScript/TypeScript
Python
Flutter
Swift
Install the Supabase JavaScript client: npm install @supabase/supabase-js
Create a Supabase client (lib/supabase.js or lib/supabase.ts): 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 a .env.local file with your credentials: NEXT_PUBLIC_SUPABASE_URL = your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY = your-anon-key
The NEXT_PUBLIC_ prefix makes variables available in the browser when using Next.js. For other frameworks, check their documentation on environment variables.
Install the Supabase Python client: Create a Supabase client: import os
from supabase import create_client, Client
url: str = os.environ.get( "SUPABASE_URL" )
key: str = os.environ.get( "SUPABASE_KEY" )
supabase: Client = create_client(url, key)
Create a .env file: SUPABASE_URL = your-project-url
SUPABASE_KEY = your-anon-key
Add Supabase to pubspec.yaml: dependencies :
supabase_flutter : ^2.0.0
Initialize Supabase in your main.dart: import 'package:supabase_flutter/supabase_flutter.dart' ;
Future < void > main () async {
WidgetsFlutterBinding . ensureInitialized ();
await Supabase . initialize (
url : 'your-project-url' ,
anonKey : 'your-anon-key' ,
);
runApp ( MyApp ());
}
// Get a reference to the client
final supabase = Supabase .instance.client;
Add Supabase to your Swift package dependencies: dependencies : [
. package (
url : "https://github.com/supabase/supabase-swift.git" ,
from : "2.0.0"
)
]
Initialize the client: import Supabase
let supabase = SupabaseClient (
supabaseURL : URL ( string : "your-project-url" ) ! ,
supabaseKey : "your-anon-key"
)
Now let’s interact with our database.
JavaScript/TypeScript
Python
Flutter
import { supabase } from './lib/supabase'
// Create a todo
const createTodo = async ( title : string , description : string ) => {
const { data , error } = await supabase
. from ( 'todos' )
. insert ({
title ,
description ,
user_id: ( await supabase . auth . getUser ()). data . user ?. id
})
. select ()
. single ()
if ( error ) throw error
return data
}
// Read todos
const getTodos = async () => {
const { data , error } = await supabase
. from ( 'todos' )
. select ( '*' )
. order ( 'created_at' , { ascending: false })
if ( error ) throw error
return data
}
// Update a todo
const updateTodo = async ( id : number , is_complete : boolean ) => {
const { error } = await supabase
. from ( 'todos' )
. update ({ is_complete })
. eq ( 'id' , id )
if ( error ) throw error
}
// Delete a todo
const deleteTodo = async ( id : number ) => {
const { error } = await supabase
. from ( 'todos' )
. delete ()
. eq ( 'id' , id )
if ( error ) throw error
}
These queries will fail until a user is authenticated because of our Row Level Security policies. Let’s add authentication next.
Step 5: Add Authentication
Supabase provides multiple authentication methods out of the box.
Email/Password Authentication
JavaScript/TypeScript
Python
Flutter
import { supabase } from './lib/supabase'
// Sign up
const signUp = async ( email : string , password : string ) => {
const { data , error } = await supabase . auth . signUp ({
email ,
password ,
})
if ( error ) throw error
return data
}
// Sign in
const signIn = async ( email : string , password : string ) => {
const { data , error } = await supabase . auth . signInWithPassword ({
email ,
password ,
})
if ( error ) throw error
return data
}
// Sign out
const signOut = async () => {
const { error } = await supabase . auth . signOut ()
if ( error ) throw error
}
// Get current user
const getUser = async () => {
const { data : { user } } = await supabase . auth . getUser ()
return user
}
// Listen to auth changes
supabase . auth . onAuthStateChange (( event , session ) => {
console . log ( event , session )
// Update your UI based on auth state
})
Magic Link Authentication
Users can sign in with just their email - no password required:
JavaScript/TypeScript
Python
Flutter
const signInWithMagicLink = async ( email : string ) => {
const { error } = await supabase . auth . signInWithOtp ({
email ,
options: {
emailRedirectTo: 'https://yourapp.com/auth/callback'
}
})
if ( error ) throw error
// User receives email with magic link
}
Social Authentication
Enable social providers in Authentication > Providers in your dashboard:
JavaScript/TypeScript
Python
Flutter
// Sign in with Google
const signInWithGoogle = async () => {
const { error } = await supabase . auth . signInWithOAuth ({
provider: 'google' ,
options: {
redirectTo: 'https://yourapp.com/auth/callback'
}
})
if ( error ) throw error
}
// Also available: github, gitlab, azure, facebook, twitter, discord, and more
Step 6: Subscribe to Realtime Changes
Get instant updates when data changes:
JavaScript/TypeScript
Python
Flutter
// Subscribe to INSERT events
const subscription = supabase
. channel ( 'todos' )
. on (
'postgres_changes' ,
{
event: 'INSERT' ,
schema: 'public' ,
table: 'todos'
},
( payload ) => {
console . log ( 'New todo created:' , payload . new )
// Update your UI with the new todo
}
)
. subscribe ()
// Subscribe to all changes
const allChanges = supabase
. channel ( 'todos' )
. on (
'postgres_changes' ,
{
event: '*' ,
schema: 'public' ,
table: 'todos'
},
( payload ) => {
console . log ( 'Change received:' , payload )
}
)
. subscribe ()
// Cleanup
supabase . removeChannel ( subscription )
Enable Realtime on your table Go to Database > Replication in your dashboard and enable realtime for the todos table.
Step 7: Deploy Your Application
Using Vercel (Recommended for Next.js)
Install Supabase Integration
Go to the Supabase Vercel Integration
Click Add Integration
Select your Vercel project and Supabase project
Environment variables are automatically configured
Deploy
Vercel automatically deploys your application with the correct environment variables.
Manual Deployment
For other platforms:
Add environment variables to your hosting platform:
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY
Configure redirect URLs in Authentication > URL Configuration :
Add your production URL to Site URL
Add redirect URLs to Redirect URLs
Deploy your application
Next Steps
Congratulations! You’ve built a full-stack application with Supabase. Here’s what to explore next:
Architecture Deep Dive Learn how all Supabase components work together
Storage Upload and serve files with automatic optimization
Edge Functions Add custom server-side logic with TypeScript
Database Functions Write PostgreSQL functions for complex logic
Common Issues
RLS policies preventing access
If you can’t read or write data:
Verify RLS is enabled: ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
Check your policies match your use case
Use the Table Editor to verify policies are active
Test with the SQL Editor using SELECT * FROM todos; while signed in
Environment variables not loading
Restart your dev server after adding .env files
Check the variable naming (e.g., NEXT_PUBLIC_ prefix for Next.js)
Verify the file is in the correct location (project root)
Don’t commit .env.local to version control
CORS errors usually mean:
Your site URL isn’t configured in Authentication > URL Configuration
You’re using the wrong URL (check for http vs https)
Your redirect URL isn’t in the allowed list
Generate TypeScript types from your database: npx supabase gen types typescript --project-id your-project-ref > types/supabase.ts
Then use them in your client: import { Database } from './types/supabase'
const supabase = createClient < Database >( url , key )
Resources