Back to Blog

Complete Guide to Supabase: Creating Tables, Schemas, and Authentication with Python & JavaScript

A comprehensive tutorial covering Supabase fundamentals, database design, authentication, and real-world implementation examples in Python and JavaScript.

Akash Aman
September 2, 2025
15 min read
Supabase
Database
Python
JavaScript
Backend
Full Stack

Complete Guide to Supabase: Creating Tables, Schemas, and Authentication with Python & JavaScript

What is Supabase?

Supabase is an open-source Backend-as-a-Service (BaaS) that provides:

  • PostgreSQL Database: Full-featured SQL database with ACID compliance
  • Auto-generated APIs: RESTful APIs and GraphQL endpoints automatically created from your schema
  • Real-time subscriptions: Live data updates across clients
  • Authentication: Built-in user management with multiple providers
  • Row Level Security (RLS): Fine-grained security policies
  • Edge Functions: Server-side logic with Deno runtime

Setting Up Your Supabase Project

1. Create a New Project

First, head to supabase.com and create a new project:

# Install Supabase CLI
npm install -g supabase
 
# Login to your account
supabase login
 
# Initialize your project
supabase init

2. Get Your Project Credentials

Navigate to your project settings and note down:

  • Project URL: https://your-project.supabase.co
  • API Key (anon): Public key for client-side usage
  • API Key (service): Private key for server-side operations

Creating Tables and Schema Design

Database Schema Planning

Let's create a comprehensive e-commerce-like schema with proper relationships:

-- Enable necessary extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
 
-- Users profile table (extends Supabase auth.users)
CREATE TABLE profiles (
  id UUID REFERENCES auth.users(id) PRIMARY KEY,
  username TEXT UNIQUE,
  full_name TEXT,
  avatar_url TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
 
-- Products table
CREATE TABLE products (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  name TEXT NOT NULL,
  description TEXT,
  price DECIMAL(10,2) NOT NULL,
  category_id UUID REFERENCES categories(id),
  image_url TEXT,
  in_stock BOOLEAN DEFAULT true,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
 
-- Categories table
CREATE TABLE categories (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  name TEXT NOT NULL UNIQUE,
  description TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
 
-- Orders table
CREATE TABLE orders (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  user_id UUID REFERENCES profiles(id) NOT NULL,
  total_amount DECIMAL(10,2) NOT NULL,
  status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'shipped', 'delivered', 'cancelled')),
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
 
-- Order items table
CREATE TABLE order_items (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
  product_id UUID REFERENCES products(id),
  quantity INTEGER NOT NULL DEFAULT 1,
  price_at_time DECIMAL(10,2) NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

Setting Up Row Level Security (RLS)

RLS is crucial for securing your data. Here's how to implement it:

-- Enable RLS on all tables
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE order_items ENABLE ROW LEVEL SECURITY;
 
-- Profiles policies
CREATE POLICY "Users can view their own profile" ON profiles
  FOR SELECT USING (auth.uid() = id);
 
CREATE POLICY "Users can update their own profile" ON profiles
  FOR UPDATE USING (auth.uid() = id);
 
CREATE POLICY "Users can insert their own profile" ON profiles
  FOR INSERT WITH CHECK (auth.uid() = id);
 
-- Products policies (public read, admin write)
CREATE POLICY "Anyone can view products" ON products
  FOR SELECT USING (true);
 
CREATE POLICY "Authenticated users can view categories" ON categories
  FOR SELECT USING (true);
 
-- Orders policies (users can only see their own orders)
CREATE POLICY "Users can view their own orders" ON orders
  FOR SELECT USING (auth.uid() = user_id);
 
CREATE POLICY "Users can insert their own orders" ON orders
  FOR INSERT WITH CHECK (auth.uid() = user_id);
 
-- Order items policies
CREATE POLICY "Users can view their own order items" ON order_items
  FOR SELECT USING (
    EXISTS (
      SELECT 1 FROM orders 
      WHERE orders.id = order_items.order_id 
      AND orders.user_id = auth.uid()
    )
  );

Authentication Setup

1. Configure Authentication Providers

In your Supabase dashboard, navigate to Authentication > Settings and configure your preferred providers:

  • Email/Password: Enable email confirmations
  • Social Providers: Google, GitHub, Discord, etc.
  • Magic Links: Passwordless authentication

2. Set Up Email Templates

Customize your email templates for:

  • Email confirmation
  • Password reset
  • Magic link

JavaScript Integration

Installation and Setup

npm install @supabase/supabase-js

Basic Configuration

// supabaseClient.js
import { createClient } from '@supabase/supabase-js'
 
const supabaseUrl = 'https://your-project.supabase.co'
const supabaseAnonKey = 'your-anon-key'
 
export const supabase = createClient(supabaseUrl, supabaseAnonKey)

Authentication with JavaScript

// auth.js
import { supabase } from './supabaseClient.js'
 
// Sign up with email and password
export async function signUp(email, password, userData = {}) {
  try {
    const { data, error } = await supabase.auth.signUp({
      email,
      password,
      options: {
        data: userData // Additional user metadata
      }
    })
    
    if (error) throw error
    return data
  } catch (error) {
    console.error('Error signing up:', error.message)
    throw error
  }
}
 
// Sign in with email and password
export async function signIn(email, password) {
  try {
    const { data, error } = await supabase.auth.signInWithPassword({
      email,
      password
    })
    
    if (error) throw error
    return data
  } catch (error) {
    console.error('Error signing in:', error.message)
    throw error
  }
}
 
// Get current user
export async function getCurrentUser() {
  try {
    const { data: { user }, error } = await supabase.auth.getUser()
    if (error) throw error
    return user
  } catch (error) {
    console.error('Error getting current user:', error.message)
    return null
  }
}

Python Integration

Installation and Setup

pip install supabase python-dotenv

Basic Configuration

# supabase_client.py
import os
from supabase import create_client, Client
from dotenv import load_dotenv
 
load_dotenv()
 
url: str = os.environ.get("SUPABASE_URL")
key: str = os.environ.get("SUPABASE_ANON_KEY")
 
supabase: Client = create_client(url, key)
 
# For server-side operations with service key
service_key: str = os.environ.get("SUPABASE_SERVICE_KEY")
admin_supabase: Client = create_client(url, service_key)

Authentication with Python

# auth.py
from supabase_client import supabase
from typing import Dict, Any, Optional
 
class AuthManager:
    def __init__(self):
        self.client = supabase
    
    def sign_up(self, email: str, password: str, user_data: Optional[Dict] = None) -> Dict[str, Any]:
        """Sign up a new user"""
        try:
            response = self.client.auth.sign_up({
                "email": email,
                "password": password,
                "options": {
                    "data": user_data or {}
                }
            })
            return response
        except Exception as e:
            print(f"Error signing up: {e}")
            raise e
    
    def sign_in(self, email: str, password: str) -> Dict[str, Any]:
        """Sign in an existing user"""
        try:
            response = self.client.auth.sign_in_with_password({
                "email": email,
                "password": password
            })
            return response
        except Exception as e:
            print(f"Error signing in: {e}")
            raise e
 
# Usage
auth = AuthManager()

Best Practices

1. Security Best Practices

  • Always enable RLS: Never expose tables without proper row-level security
  • Use service key carefully: Only use service key for admin operations on the server
  • Validate input: Always validate and sanitize user input
  • Implement rate limiting: Protect against abuse
  • Regular security audits: Review your RLS policies regularly

2. Performance Optimization

-- Add indexes for frequently queried columns
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_products_created_at ON products(created_at DESC);
 
-- Use composite indexes for complex queries
CREATE INDEX idx_orders_user_status ON orders(user_id, status);

Conclusion

Supabase provides an incredibly powerful and flexible platform for building modern applications. With its PostgreSQL foundation, automatic API generation, real-time capabilities, and robust authentication system, you can build production-ready applications quickly.

The combination of SQL flexibility, real-time subscriptions, and easy client integration makes Supabase an excellent choice for developers who want the power of a traditional database with the convenience of a modern BaaS platform.

Remember to always prioritize security with proper RLS policies, optimize for performance with appropriate indexes, and follow best practices for error handling and monitoring.

Start building with Supabase today, and experience the power of open-source backend infrastructure!