Skip to content

Supabase SDK — Using the Client Library for Database and Auth Operations

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Supabase SDK. We cover key concepts, practical examples, and best practices to help you master this topic.

The Supabase SDK provides a client library that wraps all Supabase services (database, auth, storage, realtime, and functions) into a single consistent API for JavaScript, Python, and other languages.

What You'll Learn

By the end of this lesson you will perform CRUD operations with filters and pagination, manage auth state, use the realtime subscription API, handle errors, and implement the SDK in both frontend and backend contexts.

Why It Matters

The SDK is the primary way most applications interact with Supabase. Mastering its API patterns -- query building, session management, subscriptions -- is essential for productive development.

Real-World Use

DodaZIP uses the Supabase Python SDK for backend services and the JavaScript SDK for the frontend. Both share the same API patterns, making it easy to move code between server and client.

flowchart LR
    A[Python Backend] -->|SDK| API[Supabase]
    B[JavaScript Frontend] -->|SDK| API
    C[Mobile App] -->|SDK| API
    API --> DB[(PostgreSQL)]
    API --> Auth[Auth Service]
    API --> Storage[Storage]
    style API fill:#3ecf8e,color:#fff

Querying Data

Perform database queries with the SDK.

# query_data.py
# Database queries with the Supabase SDK

import os
from supabase import create_client

url = os.getenv("SUPABASE_URL", "https://example.supabase.co")
key = os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
supabase = create_client(url, key)

def basic_queries():
    # Get all rows
    result = supabase.table("files").select("*").execute()
    print(f"All files: {len(result.data)} rows")
    
    # Get with filter
    result = supabase.table("files") \
        .select("*") \
        .eq("user_id", "user_abc") \
        .execute()
    print(f"User files: {len(result.data)} rows")
    
    # Get single row
    result = supabase.table("files") \
        .select("*") \
        .eq("id", "file_123") \
        .single() \
        .execute()
    print(f"Single file: {result.data}")
    
    # Order and limit
    result = supabase.table("files") \
        .select("*") \
        .order("created_at", desc=True) \
        .limit(10) \
        .execute()
    print(f"Latest 10 files: {len(result.data)} rows")

basic_queries()

Insert, Update, and Delete

CRUD operations with the SDK.

# crud_operations.py
# Create, update, and delete operations

def crud_examples():
    supabase = create_client(
        os.getenv("SUPABASE_URL", "https://example.supabase.co"),
        os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
    )
    
    # Insert a new file record
    data = {
        "user_id": "user_abc",
        "name": "report.pdf",
        "size_bytes": 1024000,
        "mime_type": "application/pdf",
    }
    result = supabase.table("files").insert(data).execute()
    print(f"Inserted file: {result.data}")
    
    # Update a record
    result = supabase.table("files") \
        .update({"status": "processed"}) \
        .eq("id", "file_123") \
        .execute()
    print(f"Updated file: {result.data}")
    
    # Delete a record
    result = supabase.table("files") \
        .delete() \
        .eq("id", "file_456") \
        .execute()
    print(f"Deleted file count: {len(result.data)}")
    
    # Upsert (insert or update)
    result = supabase.table("files").upsert({
        "id": "file_789",
        "name": "updated-report.pdf",
        "size_bytes": 2048000,
    }).execute()
    print(f"Upserted file: {result.data}")

crud_examples()

Authentication with SDK

Manage user auth through the SDK.

# auth_sdk.py
# Authentication operations with the SDK

def auth_examples():
    supabase = create_client(
        os.getenv("SUPABASE_URL", "https://example.supabase.co"),
        os.getenv("SUPABASE_ANON_KEY", "your-anon-key")
    )
    
    # Get current session
    session = supabase.auth.get_session()
    if session:
        print(f"Logged in as: {session.user.email}")
    else:
        print("Not logged in")
    
    # Listen for auth state changes
    def on_auth_state_change(event, session):
        print(f"Auth event: {event}")
        if session:
            print(f"User: {session.user.email}")
    
    subscription = supabase.auth.on_auth_state_change(on_auth_state_change)
    print("Listening for auth state changes...")
    
    # Update user metadata
    result = supabase.auth.update_user({
        "data": {"full_name": "Alice Johnson"}
    })
    print(f"Updated user metadata: {result}")

auth_examples()

Error Handling

Properly handle SDK errors.

# error_handling.py
# Handling SDK errors

from supabase import create_client
from supabase.lib.client_options import ClientOptions

def handle_sdk_errors():
    supabase = create_client(
        "https://example.supabase.co",
        "invalid-key"
    )
    
    try:
        result = supabase.table("files").select("*").execute()
        print(f"Query succeeded: {result}")
    except Exception as e:
        error_msg = str(e)
        if "401" in error_msg:
            print(f"Auth error: Invalid API key or session")
        elif "404" in error_msg:
            print(f"Not found: Table or row does not exist")
        elif "400" in error_msg:
            print(f"Bad request: {e}")
        elif "row-level security" in error_msg.lower():
            print(f"Policy error: RLS policy blocked the operation")
        else:
            print(f"Unexpected error: {e}")

handle_sdk_errors()

Common Mistakes

  1. Not awaiting queries: The SDK returns promises in JavaScript and coroutines in Python. Forgetting to await results in accessing pending promises.

  2. Using service_role key on the client: The service_role key bypasses RLS. Use the anon key for client-side code and the service_role key only in secure backend contexts.

  3. Not handling authentication errors: Failed auth operations throw exceptions. Always wrap auth calls in try/catch blocks.

  4. Chaining methods incorrectly: The SDK uses method chaining. Operations like .eq().order().limit() must be chained in the correct order after .select().

  5. Forgetting to enable RLS on new tables: If you insert data via the SDK without RLS enabled, users may see data they should not have access to.

Practice Questions

  1. How do you query rows where a column equals a specific value? Use .eq("column", "value") chained after .select("*").

  2. How do you insert multiple rows at once? Pass a list of dictionaries to .insert().

  3. How do you update rows matching a condition? Chain .update({"column": "value"}).eq("id", "target_id").

  4. What is the difference between insert and upsert? Upsert inserts a new row or updates an existing one if it conflicts on a unique constraint.

  5. Challenge: Build a complete CRUD API wrapper using the Supabase SDK that handles authentication, validation, error handling, and pagination for a file management system.

FAQ

What languages does the Supabase SDK support?

Official SDKs exist for JavaScript, Python, Dart/Flutter, Swift, and Kotlin.

Can I use the SDK on the server side?

Yes. The Python SDK is designed for server use. The JavaScript SDK works in Node.js and Deno.

Does the SDK support transactions?

Not directly. Use PostgreSQL functions or Edge Functions for transactional operations.

How do I handle pagination?

Use .range(start, end) to paginate results. The SDK provides count options for total row counts.

What is the .single() method for?

It tells the SDK to expect exactly one row. It returns an error if zero or multiple rows are found.

Mini Project

Create a data access layer using the Supabase SDK that provides typed CRUD operations, authentication, and error handling for the file management system.

class FileRepository:
    def __init__(self, supabase_client):
        self.supabase = supabase_client
        self.table = supabase_client.table("files")
    
    def get_user_files(self, user_id, page=1, per_page=20):
        start = (page - 1) * per_page
        end = start + per_page - 1
        result = self.table.select("*") \
            .eq("user_id", user_id) \
            .order("created_at", desc=True) \
            .range(start, end) \
            .execute()
        return result.data
    
    def create_file(self, file_data):
        result = self.table.insert(file_data).execute()
        return result.data[0]
    
    def delete_file(self, file_id, user_id):
        result = self.table.delete() \
            .eq("id", file_id) \
            .eq("user_id", user_id) \
            .execute()
        return len(result.data) > 0

# Usage
repo = FileRepository(supabase)
files = repo.get_user_files("user_abc")
print(f"Retrieved {len(files)} files")

What's Next

Next: Supabase REST API for direct API access without the SDK.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro