Upgrade Supabase SDK and CLI versions with breaking-change detection and automated code migration. Use when upgrading @supabase/supabase-js (v1→v2 or minor bumps), migrating auth/realtime/storage APIs, or updating the Supabase CLI. Trigger with phrases like "upgrade supabase", "supabase breaking changes", "migrate supabase v2", "update supabase SDK".
Upgrade @supabase/supabase-js and the Supabase CLI with breaking-change detection, automated code migration, and rollback planning. Covers the v1-to-v2 migration path (auth method renames, data/error destructuring, realtime API overhaul), minor version bumps, @supabase/ssr adoption, and Python SDK upgrades via pip install --upgrade supabase.
!npm list @supabase/supabase-js 2>/dev/null | grep supabase || echo 'supabase-js not installed'
!supabase --version 2>/dev/null || echo 'CLI not installed'
!pip show supabase 2>/dev/null | grep Version || echo 'Python SDK not installed'
@supabase/supabase-js or the Python supabase package installed in the projectCheck every installed Supabase package and find all import sites in the codebase.
# Check current SDK version
npm list @supabase/supabase-js
# Check CLI version
supabase --version
# Check Python SDK version
pip show supabase | grep Version
# Find all JS/TS Supabase imports
grep -rn "from '@supabase/supabase-js'" --include="*.ts" --include="*.tsx" --include="*.js" src/ lib/ app/ 2>/dev/null
grep -rn "createClient" --include="*.ts" --include="*.tsx" --include="*.js" src/ lib/ app/ 2>/dev/null
# Find all Python Supabase imports
grep -rn "from supabase" --include="*.py" src/ app/ 2>/dev/null
supabase-js v1 → v2 breaking changes:
| v1 Pattern | v2 Replacement | Notes |
|---|---|---|
createClient(url, key) | createClient(url, key) | Signature unchanged, but return type differs |
supabase.auth.session() | supabase.auth.getSession() | Sync → async, returns { data: { session } } |
supabase.auth.user() | supabase.auth.getUser() | Sync → async, returns { data: { user } } |
supabase.auth.signIn({ email, password }) | supabase.auth.signInWithPassword({ email, password }) | Method split by auth type |
supabase.auth.signIn({ provider: 'google' }) | supabase.auth.signInWithOAuth({ provider: 'google' }) | OAuth separated |
supabase.auth.signIn({ email }) | supabase.auth.signInWithOtp({ email }) | Magic link separated |
supabase.auth.api.resetPasswordForEmail(e) | supabase.auth.resetPasswordForEmail(e) | .api namespace removed |
{ data: subscription } from onAuthStateChange | { data: { subscription } } | Extra destructuring level |
error.message string parsing | error.code enum (PGRST116, etc.) | Reliable error matching |
.single() returns error on 0 rows | .maybeSingle() for optional rows | New method for nullable results |
supabase.from('t').on('INSERT', cb).subscribe() | supabase.channel('c').on('postgres_changes', ...).subscribe() | Realtime v2 channel API |
supabase.storage.from('b').download('path') | Same, but returns { data: Blob, error } | Consistent error/data tuple |
Realtime v2 migration detail:
// v1 realtime
supabase
.from('messages')
.on('INSERT', (payload) => console.log(payload.new))
.subscribe()
// v2 realtime — channel-based API
supabase
.channel('messages-insert')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => console.log(payload.new))
.subscribe()
Create a branch, install new packages, and transform code to match v2 APIs.
# Create upgrade branch
git checkout -b upgrade-supabase-sdk
# Upgrade JS/TS SDK
npm install @supabase/supabase-js@latest
# Upgrade SSR helper (if used with Next.js/SvelteKit/Nuxt)
npm install @supabase/ssr@latest
# Upgrade CLI
npm install -g supabase@latest
# Upgrade Python SDK
pip install --upgrade supabase
# Regenerate TypeScript types from linked project
npx supabase gen types typescript --linked > lib/database.types.ts
# Generate a database migration if schema drifted
npx supabase db diff --use-migra -f upgrade_check
Apply auth code migrations:
// BEFORE (v1 auth patterns)
const session = supabase.auth.session()
const user = supabase.auth.user()
const { error } = await supabase.auth.signIn({ email, password })
const { data: subscription } = supabase.auth.onAuthStateChange(callback)
// AFTER (v2 auth patterns)
const { data: { session } } = await supabase.auth.getSession()
const { data: { user } } = await supabase.auth.getUser()
const { error } = await supabase.auth.signInWithPassword({ email, password })
const { data: { subscription } } = supabase.auth.onAuthStateChange(callback)
Apply error handling migration:
// BEFORE (v1 — string matching)
if (error.message.includes('not found')) { ... }
// AFTER (v2 — structured error codes)
if (error.code === 'PGRST116') { ... } // "not found" → PGRST116
# Type check (catches 90% of migration issues)
npx tsc --noEmit
# Run test suite
npm test
# Python tests
python -m pytest tests/ -v
# Manual smoke test critical auth flows:
# 1. Sign up → confirm email → sign in with password
# 2. OAuth sign in → callback handling
# 3. Password reset → email → reset form
# 4. Session refresh across page navigations
# 5. Realtime subscription connect/disconnect
# 6. Storage upload/download round-trip
Rollback procedure (if upgrade causes issues):
# Option A: Pin to previous version
npm install @supabase/supabase-js@<previous-version>
pip install supabase==<previous-version>
# Option B: Revert the branch
git stash && git checkout main
@supabase/supabase-js upgraded to latest version with npm list confirmationsupabase.auth.signIn() calls migrated to signInWithPassword / signInWithOAuth / signInWithOtpsession(), user()) replaced with async getSession() / getUser().on() to channel-based APIdata/error destructuring updated where return shapes changed| Error | Cause | Solution |
|---|---|---|
Property 'session' does not exist | v1 sync .session() removed in v2 | Replace with await supabase.auth.getSession() |
Property 'signIn' does not exist | signIn split into multiple methods in v2 | Use signInWithPassword, signInWithOAuth, or signInWithOtp |
supabase.auth.api is undefined | .api namespace removed in v2 | Call methods directly on supabase.auth.* |
TypeError: supabase.from(...).on is not a function | Realtime API replaced in v2 | Use supabase.channel().on('postgres_changes', ...) |
Type errors after gen types | Database schema changed between versions | Update application code to match new generated types |
PGRST116 error on .single() | Zero rows returned (v2 throws) | Use .maybeSingle() for optional lookups |
ERR_REQUIRE_ESM after upgrade | v2 is ESM-only in some bundlers | Update tsconfig.json to "module": "esnext" or use dynamic import() |
AuthSessionMissingError | getSession() called before auth initialized | Wrap in onAuthStateChange listener or check session !== null |
Full v1 → v2 auth migration (Next.js):
// lib/supabase.ts — client initialization (unchanged API)
import { createClient } from '@supabase/supabase-js'
import type { Database } from './database.types'
export const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// app/login/page.tsx — v2 auth flow
export async function login(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
// v2: use error.code instead of parsing error.message
if (error.code === 'invalid_credentials') {
return { success: false, message: 'Invalid email or password' }
}
throw error
}
return { success: true, session: data.session }
}
// hooks/useAuth.ts — v2 session listener
import { useEffect, useState } from 'react'
import { supabase } from '@/lib/supabase'
import type { Session } from '@supabase/supabase-js'
export function useAuth() {
const [session, setSession] = useState<Session | null>(null)
useEffect(() => {
// v2: getSession is async, returns nested { data: { session } }
supabase.auth.getSession().then(({ data: { session } }) => {
setSession(session)
})
// v2: subscription nested one level deeper
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, session) => setSession(session)
)
return () => subscription.unsubscribe()
}, [])
return session
}
Python SDK upgrade:
# Before (supabase-py < 2.0)
from supabase import create_client
supabase = create_client(url, key)
data = supabase.table("users").select("*").execute()
users = data["data"]
# After (supabase-py >= 2.0)
from supabase import create_client, Client