belajarkoding © 2025 BelajarKoding. All rights reserved.
Supabase Cheat Sheet - BelajarKoding | BelajarKoding
Supabase Cheat Sheet Quick reference Supabase. Setup, authentication, database queries, real-time subscriptions, storage, Row Level Security, dan best practices untuk building modern apps.
JavaScript 11 min read 2.192 kata
Lanjutkan Membaca
Daftar gratis untuk akses penuh ke semua artikel dan cheat sheet. Cepat, mudah, dan tanpa biaya!
Akses Tanpa Batas
Baca semua artikel dan cheat sheet kapan pun kamu mau
Bookmark Konten
Simpan artikel dan roadmap favoritmu untuk dibaca nanti
Gratis Selamanya
Tidak ada biaya tersembunyi, 100% gratis
Dengan mendaftar, kamu setuju dengan syarat dan ketentuan kami
# Setup & Installation
# Install Supabase Client
# npm
npm install @supabase/supabase-js
# pnpm
pnpm add @supabase/supabase-js
# yarn
yarn add @supabase/supabase-js
# Initialize Client
// 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) # .env.local
NEXT_PUBLIC_SUPABASE_URL = https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY = eyJhbGc...
SUPABASE_SERVICE_ROLE_KEY = eyJhbGc... # Server-side only # Server-Side Client (Next.js)// lib/supabase-server.ts
import { createClient } from '@supabase/supabase-js'
export const supabaseAdmin = createClient (
process.env. NEXT_PUBLIC_SUPABASE_URL ! ,
process.env. SUPABASE_SERVICE_ROLE_KEY ! , // Bypass RLS
{
auth: {
autoRefreshToken:
// Get all rows
const { data , error } = await supabase
. from ( 'posts' )
. select ( '*' )
// Select specific columns
// Insert single row
const { data , error } = await supabase
. from ( 'posts' )
. insert ({ title: 'New Post' , content: 'Hello World' })
. select
// Update by ID
const { data , error } = await supabase
. from ( 'posts' )
. update ({ title: 'Updated Title' })
. eq ( 'id' , 1 )
// Delete by ID
const { error } = await supabase
. from ( 'posts' )
. delete ()
. eq ( 'id' , 1 )
// Delete with conditions
const {
// Comparison operators
. eq ( 'column' , value) // Equal
. neq ( 'column' , value) // Not equal
. gt ( 'column' , value) // Greater than
. gte
# Sign Up (Email/Password)const { data , error } = await supabase.auth. signUp ({
email: 'user@example.com' ,
password: 'password123' ,
options: {
data: {
full_name: 'John Doe' ,
age: 25
}
}
// Email & Password
const { data , error } = await supabase.auth. signInWithPassword ({
email: 'user@example.com' ,
password: 'password123'
})
// Magic Link (Passwordless)
const {
const { error } = await supabase.auth. signOut () // Get user from session
const { data : { user } } = await supabase.auth. getUser ()
// Get session
const { data : { session } } = await supabase.auth. getSession () const { data : { subscription } } = supabase.auth. onAuthStateChange (
( event , session ) => {
console. log (event, session)
// events: 'SIGNED_IN', 'SIGNED_OUT', 'TOKEN_REFRESHED', etc.
}
)
// Cleanup
subscription. // Send reset email
const { data , error } = await supabase.auth. resetPasswordForEmail (
'user@example.com' ,
{
redirectTo: 'https://example.com/reset-password'
}
)
// Update password (after reset)
const { data
const { data , error } = await supabase.auth. updateUser ({
data: {
full_name: 'Jane Doe' ,
avatar_url: 'https://example.com/avatar.jpg'
}
})
# Subscribe to Table Changes// Subscribe to all changes
const subscription = supabase
. channel ( 'posts-channel' )
. on (
'postgres_changes' ,
{
event: '*' , // '*', 'INSERT', 'UPDATE', 'DELETE'
schema: 'public' ,
# Subscribe to Specific Rowconst subscription = supabase
. channel ( 'post-1' )
. on (
'postgres_changes' ,
{
event: 'UPDATE' ,
schema: 'public' ,
table: 'posts' ,
filter:
# Multiple Listeners on One Channelconst channel = supabase. channel ( 'room-1' )
// Listen to inserts
channel. on (
'postgres_changes' ,
{ event: 'INSERT' , schema: 'public' , table: 'messages' },
( payload )
# Broadcast (Custom Events)// Send message
await channel. send ({
type: 'broadcast' ,
event: 'cursor-pos' ,
payload: { x: 100 , y: 200 }
})
// Listen to broadcast
channel. on ( 'broadcast' , { event: 'cursor-pos' }, (
# Presence (Track Online Users)const channel = supabase. channel ( 'room-1' )
// Track user presence
await channel. subscribe ( async ( status ) => {
if (status
// Upload file
const { data , error } = await supabase.storage
. from ( 'avatars' )
. upload ( 'user-1.png' , file, {
cacheControl: '3600' ,
upsert: true
})
// Download file
const { data , error } = await supabase.storage
. from ( 'avatars' )
. download ( 'user-1.png' )
// Get public URL
const { data
const { data , error } = await supabase.storage
. from ( 'avatars' )
. list ( 'public' , {
limit: 100 ,
offset: 0 ,
sortBy: { column: 'name' , order:
// Delete single file
const { data , error } = await supabase.storage
. from ( 'avatars' )
. remove ([ 'user-1.png' ])
// Delete multiple files
const { data , error
// Move file
const { data , error } = await supabase.storage
. from ( 'avatars' )
. move ( 'old-path.png' , 'new-path.png' )
// Copy file
const { data ,
# Row Level Security (RLS)-- Enable RLS on table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY ; -- Allow anyone to read
CREATE POLICY "Public posts are viewable by everyone"
ON posts FOR SELECT
USING (true);
-- Users can only see their own data
CREATE POLICY "Users can view own posts"
ON
# Bypass RLS (Server-side)// Use service role key to bypass RLS
import { createClient } from '@supabase/supabase-js'
const supabaseAdmin = createClient (
process.env. NEXT_PUBLIC_SUPABASE_URL ! ,
process.env. SUPABASE_SERVICE_ROLE_KEY !
)
// Now queries bypass RLS
const { data
# Using Supabase CLI
supabase functions new my-function // supabase/functions/my-function/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve ( async ( req ) =>
supabase functions deploy my-function // From client
const { data , error } = await supabase.functions. invoke ( 'my-function' , {
body: { name: 'John' }
})
// With custom headers
const { data , error }
# Generate Types from Database# Install Supabase CLI
npm install supabase --save-dev
# Login
npx supabase login
# Generate types
npx supabase gen types typescript --project-id YOUR_PROJECT_ID > types/database.ts // types/database.ts is auto-generated
import { Database } from '@/types/database'
// Create typed client
export const supabase = createClient < Database >(
supabaseUrl,
supabaseAnonKey
)
// Now queries are fully typed!
const { data
import { Database } from '@/types/database'
type Post = Database [ 'public' ][ 'Tables' ][ 'posts' ][ 'Row' ]
type PostInsert = Database [ 'public' ][ 'Tables' ][
# Protected Route (Next.js App Router)// app/dashboard/page.tsx
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
export default async function Dashboard () {
const supabase
import { useState, useEffect } from 'react'
function usePagination ( table : string , pageSize = 10 ) {
const [ data , setData ] = useState
async function toggleTodo ( id : string , isComplete : boolean ) {
// Update UI immediately
setTodos ( prev =>
prev. map ( t => t.id ===
import { useState, useEffect } from 'react'
import { useDebounce } from 'use-debounce'
function SearchPosts () {
const [ query , setQuery ] = useState
✅ Always enable Row Level Security (RLS) on tables
✅ Use service role key only on server-side
✅ Never expose service role key to client
✅ Validate user input before queries
✅ Use policies to restrict data access
✅ Use indexes for frequently queried columns
✅ Select only columns you need (select('id, title') )
✅ Implement pagination for large datasets
✅ Use .single() when fetching one row
✅ Enable PostgREST cache headers
✅ Unsubscribe from channels when component unmounts
✅ Use filters to reduce unnecessary events
✅ Implement reconnection logic for unstable networks
✅ Use presence for tracking online users efficiently
✅ Use transactions for related operations
✅ Add constraints (UNIQUE, NOT NULL, CHECK)
✅ Create proper foreign key relationships
✅ Use UUID for primary keys
✅ Add created_at and updated_at timestamps
# Supabase CLI
supabase init # Initialize Supabase project
supabase start # Start local Supabase
supabase stop # Stop local Supabase
supabase db reset # Reset local database
supabase db push # Push migrations to remote
supabase gen types
false
,
persistSession: false
}
}
)
const
{
data
}
=
await
supabase
. from ( 'posts' )
. select ( 'id, title, author' )
// Filter by condition
const { data } = await supabase
. from ( 'posts' )
. select ( '*' )
. eq ( 'status' , 'published' )
. gt ( 'views' , 100 )
// Join with other tables
const { data } = await supabase
. from ( 'posts' )
. select ( `
id,
title,
author:users (name, email)
` )
// Pagination
const { data } = await supabase
. from ( 'posts' )
. select ( '*' )
. range ( 0 , 9 ) // First 10 items
()
// Insert multiple rows
const { data } = await supabase
. from ( 'posts' )
. insert ([
{ title: 'Post 1' },
{ title: 'Post 2' }
])
. select ()
// Upsert (insert or update if exists)
const { data } = await supabase
. from ( 'posts' )
. upsert ({ id: 1 , title: 'Updated' })
. select ()
. select ()
// Update with conditions
const { data } = await supabase
. from ( 'posts' )
. update ({ status: 'archived' })
. lt ( 'created_at' , '2024-01-01' )
error
}
=
await
supabase
. from ( 'posts' )
. delete ()
. eq ( 'status' , 'draft' )
. lt ( 'created_at' , '2024-01-01' )
(
'column'
, value)
// Greater than or equal
. lt ( 'column' , value) // Less than
. lte ( 'column' , value) // Less than or equal
// Pattern matching
. like ( 'column' , '%pattern%' ) // Case-sensitive LIKE
. ilike ( 'column' , '%pattern%' ) // Case-insensitive LIKE
// In array
. in ( 'column' , [value1, value2])
// Null checks
. is ( 'column' , null )
. not ( 'column' , 'is' , null )
// Full text search
. textSearch ( 'column' , 'keyword' )
// Order & Limit
. order ( 'created_at' , { ascending: false })
. limit ( 10 )
})
data
,
error
}
=
await
supabase.auth.
signInWithOtp
({
email: 'user@example.com' ,
options: {
emailRedirectTo: 'https://example.com/auth/callback'
}
})
// OAuth (Google, GitHub, etc.)
const { data , error } = await supabase.auth. signInWithOAuth ({
provider: 'google' ,
options: {
redirectTo: 'https://example.com/auth/callback'
}
})
unsubscribe
()
,
error
}
=
await
supabase.auth.
updateUser
({
password: 'new_password'
})
table: 'posts'
},
( payload ) => {
console. log ( 'Change received!' , payload)
}
)
. subscribe ()
// Cleanup
subscription. unsubscribe ()
'id=eq.1'
},
( payload ) => {
console. log ( 'Post 1 updated!' , payload)
}
)
. subscribe ()
=>
console.
log
(
'New message'
, payload)
)
// Listen to presence
channel. on ( 'presence' , { event: 'sync' }, () => {
const state = channel. presenceState ()
console. log ( 'Online users:' , state)
})
channel. subscribe ()
payload
)
=>
{
console. log ( 'Cursor moved:' , payload)
})
===
'SUBSCRIBED'
) {
await channel. track ({
user_id: 'user-1' ,
online_at: new Date (). toISOString ()
})
}
})
// Listen to presence changes
channel. on ( 'presence' , { event: 'sync' }, () => {
const state = channel. presenceState ()
console. log ( 'Online:' , state)
})
channel. on ( 'presence' , { event: 'join' }, ({ key , newPresences }) => {
console. log ( 'User joined:' , newPresences)
})
channel. on ( 'presence' , { event: 'leave' }, ({ key , leftPresences }) => {
console. log ( 'User left:' , leftPresences)
})
// Upload with path
const { data , error } = await supabase.storage
. from ( 'documents' )
. upload ( 'public/doc.pdf' , file)
}
=
supabase.storage
. from ( 'avatars' )
. getPublicUrl ( 'user-1.png' )
console. log (data.publicUrl)
// Create signed URL (private buckets)
const { data , error } = await supabase.storage
. from ( 'private' )
. createSignedUrl ( 'secret.pdf' , 60 ) // Valid for 60 seconds
'asc'
}
})
}
=
await
supabase.storage
. from ( 'avatars' )
. remove ([ 'user-1.png' , 'user-2.png' ])
error
}
=
await
supabase.storage
. from ( 'avatars' )
. copy ( 'source.png' , 'destination.png' )
posts
FOR
SELECT
USING ( auth . uid () = user_id);
-- Users can insert their own data
CREATE POLICY "Users can insert own posts"
ON posts FOR INSERT
WITH CHECK ( auth . uid () = user_id);
-- Users can update their own data
CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
USING ( auth . uid () = user_id)
WITH CHECK ( auth . uid () = user_id);
-- Users can delete their own data
CREATE POLICY "Users can delete own posts"
ON posts FOR DELETE
USING ( auth . uid () = user_id);
-- Check user role
CREATE POLICY "Only admins can delete"
ON posts FOR DELETE
USING (
auth . uid () IN (
SELECT id FROM users WHERE role = 'admin'
)
);
}
=
await
supabaseAdmin
. from ( 'posts' )
. select ( '*' ) // Returns all rows regardless of RLS
{
try {
// Get data from request
const { name } = await req. json ()
// Initialize Supabase client
const supabase = createClient (
Deno.env. get ( 'SUPABASE_URL' ) ?? '' ,
Deno.env. get ( 'SUPABASE_ANON_KEY' ) ?? ''
)
// Query database
const { data , error } = await supabase
. from ( 'users' )
. select ( '*' )
. eq ( 'name' , name)
if (error) throw error
return new Response (
JSON . stringify ({ data }),
{ headers: { 'Content-Type' : 'application/json' } }
)
} catch (error) {
return new Response (
JSON . stringify ({ error: error.message }),
{ status: 400 , headers: { 'Content-Type' : 'application/json' } }
)
}
})
=
await
supabase.functions.
invoke
(
'my-function'
, {
body: { name: 'John' },
headers: {
'Authorization' : `Bearer ${ token }`
}
})
}
=
await
supabase
. from ( 'posts' ) // Autocomplete available
. select ( '*' )
// data is typed as Post[]
'posts'
][
'Insert'
]
type PostUpdate = Database [ 'public' ][ 'Tables' ][ 'posts' ][ 'Update' ]
// Use in components
function PostList ({ posts } : { posts : Post [] }) {
// ...
}
=
createServerComponentClient
({ cookies })
const { data : { session } } = await supabase.auth. getSession ()
if ( ! session) {
redirect ( '/login' )
}
return < div >Protected content </ div >
}
([])
const [ page , setPage ] = useState ( 0 )
const [ loading , setLoading ] = useState ( false )
useEffect (() => {
async function fetchData () {
setLoading ( true )
const { data } = await supabase
. from (table)
. select ( '*' )
. range (page * pageSize, (page + 1 ) * pageSize - 1 )
setData (data || [])
setLoading ( false )
}
fetchData ()
}, [page, table])
return { data, loading, page, setPage }
}
id
?
{
...
t, is_complete:
!
isComplete }
:
t)
)
// Update database
const { error } = await supabase
. from ( 'todos' )
. update ({ is_complete: ! isComplete })
. eq ( 'id' , id)
// Revert on error
if (error) {
setTodos ( prev =>
prev. map ( t => t.id === id ? { ... t, is_complete } : t)
)
toast. error ( 'Failed to update' )
}
}
(
''
)
const [ debouncedQuery ] = useDebounce (query, 500 )
const [ results , setResults ] = useState ([])
useEffect (() => {
if ( ! debouncedQuery) {
setResults ([])
return
}
async function search () {
const { data } = await supabase
. from ( 'posts' )
. select ( '*' )
. ilike ( 'title' , `%${ debouncedQuery }%` )
setResults (data || [])
}
search ()
}, [debouncedQuery])
return (
<>
< input value = {query} onChange = { e => setQuery (e.target.value)} />
{ results . map ( post => < div key ={ post . id }>{post.title} </ div > )}
</>
)
}
typescript
# Generate types
supabase functions new # Create new Edge Function
supabase functions deploy # Deploy Edge Function