Why PostgreSQL Matters
PostgreSQL has earned its reputation as one of the world's most stable and advanced databases over decades of production use. Supabase builds entirely on this foundation, giving you access to every PostgreSQL feature while adding platform conveniences that streamline development workflows.
Every Supabase project includes a full Postgres database with postgres-level access, meaning you have complete control over your schema, functions, and advanced features. This isn't a simplified or restricted version of PostgreSQL--it's the real thing, ready for any use case from simple CRUD applications to complex analytical workloads. For teams building custom web applications, this provides the robust data layer needed for production systems.
Key PostgreSQL Advantages
- Rich Type System: Support for simple types and complex structures including JSON, arrays, and custom domains
- Sophisticated Query Optimization: Efficient handling of joins, subqueries, window functions, and recursive CTEs
- Extensible Architecture: Transform PostgreSQL into purpose-built platforms with specialized extensions
Everything you need to build and manage production databases
Full PostgreSQL Access
Complete control over your database with postgres-level privileges and all PostgreSQL features
Visual Table Editor
Create and modify tables without writing SQL, with full SQL visibility available
SQL Editor
Run queries, save favorites, and collaborate with team members on database operations
Schema Relationships
Visualize and manage foreign key relationships between your tables
Backup Management
Automatic backups with point-in-time recovery options for data protection
Extensions Marketplace
Enable powerful extensions like pgvector, PostGIS, and pg_cron with one click
Schema Design Fundamentals
Effective schema design forms the foundation of any robust application. Supabase provides multiple ways to design and modify your schema, accommodating developers of all experience levels.
Table Creation and Management
Supabase offers an intuitive table editor that makes PostgreSQL accessible without requiring deep SQL knowledge. You can create tables, define columns, set data types, add constraints, and establish relationships through a visual interface. Each table you create generates the appropriate SQL statements behind the scenes, and you can view or modify the underlying schema at any time.
For developers who prefer direct SQL access, the built-in SQL editor provides a powerful environment for running queries and managing schema. The SQL editor supports saved queries, allowing you to store frequently used queries for quick access. You can also share queries with team members, making it easier to collaborate on database operations. This approach is essential when working on complex web development projects that require custom database logic.
Relationships Between Tables
PostgreSQL's relational model shines when representing complex relationships between entities:
| Relationship Type | Description | Example |
|---|---|---|
| One-to-One | Single row correspondence between tables | users → profiles |
| One-to-Many | One row relates to multiple others | customers → orders |
| Many-to-Many | Bidirectional relationships via junction table | students → courses |
Data Types and Constraints
Choosing appropriate data types and constraints ensures data integrity and optimizes storage. PostgreSQL provides types for integers, decimals, text, dates, timestamps, UUIDs, JSON, and arrays.
Key Constraints:
- NOT NULL: Prevents null values in a column
- UNIQUE: Ensures all values are distinct
- PRIMARY KEY: Uniquely identifies each row
- FOREIGN KEY: Maintains referential integrity
- CHECK: Validates values against conditions
- DEFAULT: Provides fallback values
Database Migrations
Migrations provide a version-controlled approach to schema changes, enabling teams to track, review, and deploy database updates reliably. Supabase's migration system integrates with the CLI for local development and connects seamlessly with CI/CD pipelines for production deployments.
Migration Workflow
- Create Migration: Generate a new migration file using the CLI or Dashboard
- Write SQL: Add schema change statements--CREATE TABLE, ALTER COLUMN, etc.
- Apply Locally: Run
supabase migration upto apply changes to local database - Test Thoroughly: Verify changes work as expected with application code
- Deploy: Push migrations to production via CLI or CI/CD pipeline
Example Migration
-- Create a new table
create table if not exists employees (
id bigint primary key generated always as identity,
name text not null,
email text,
department text default 'Engineering',
created_at timestamptz default now()
);
-- Add a new column
alter table if exists employees
add salary numeric;
Best Practices
- Keep migrations small and focused on single changes
- Make migrations idempotent (safe to run multiple times)
- Test migrations in staging before production deployment
- Use schema diffing for Dashboard-made changes
- Include seed data in seed.sql for development consistency
For teams managing complex deployments, understanding Supabase local development workflows helps streamline the migration process.
Triggers and Functions
PostgreSQL's procedural language capabilities allow you to encapsulate complex logic in the database layer. Triggers and functions work together to automate data operations, enforce business rules, and maintain data integrity.
Database Functions
A database function is a named sequence of SQL statements that accepts parameters, performs operations, and returns results. PostgreSQL supports PL/pgSQL as the most common procedural language.
Creating Triggers
Triggers automatically execute in response to INSERT, UPDATE, DELETE, or TRUNCATE events:
| Timing | Description |
|---|---|
| BEFORE | Fires before the operation, can modify NEW values |
| AFTER | Fires after the operation completes |
Trigger Variables
| Variable | Description |
|---|---|
| TG_TABLE_NAME | Name of the triggering table |
| TG_WHEN | BEFORE or AFTER the event |
| TG_OP | INSERT, UPDATE, DELETE, or TRUNCATE |
| OLD | Previous row values (UPDATE/DELETE) |
| NEW | New row values (INSERT/UPDATE) |
Example: Audit Log Trigger
create or replace function audit_trigger_function()
returns trigger as $$
begin
insert into audit_log (table_name, action, old_data, new_data)
values (tg_table_name, tg_op, old, new);
return new;
end;
$$ language plpgsql;
create trigger audit_changes
after insert or update or delete on users
for each row execute function audit_trigger_function();
Common Use Cases
- Audit Logging: Automatically record changes to sensitive tables
- Data Synchronization: Keep related tables and caches in sync
- Complex Validation: Enforce rules spanning multiple columns
- Automated Calculations: Maintain computed columns and aggregates
For security-focused implementations, combine triggers with Row Level Security policies to create comprehensive data protection layers.
Extensions Ecosystem
PostgreSQL's extension system allows you to enhance the database with specialized capabilities. Supabase makes it easy to enable extensions with a single click in the Dashboard.
Popular Extensions
| Extension | Purpose |
|---|---|
| pgvector | Vector similarity search for AI embeddings and semantic search |
| PostGIS | Geospatial queries for mapping and location-based applications |
| pg_cron | Scheduled job execution within the database |
| pg_graphql | GraphQL API exposure for your schema |
| pg_stat_statements | Query execution statistics and performance analysis |
| pgTAP | Unit testing for your database code |
| pg_net | Async HTTP requests from within PostgreSQL |
Enabling Extensions
Most extensions can be enabled with a single SQL command:
create extension if not exists pgvector;
create extension if not exists postgis;
create extension if not exists pg_cron;
Or enable them directly from the Supabase Dashboard with one click.
Use Case Examples
AI & Machine Learning: Use pgvector to store embeddings and perform similarity searches for recommendation systems, semantic search, and retrieval-augmented generation (RAG) pipelines. This is particularly valuable for teams implementing AI automation solutions that require vector storage and similarity search capabilities.
Geospatial Applications: PostGIS enables storing geographic data, performing spatial queries, and building location-aware features like store finders and territory management.
Background Jobs: pg_cron schedules recurring tasks like data cleanup, report generation, and periodic data aggregation without external job runners.
Explore how these extensions integrate with your Supabase authentication system to build powerful, secure applications.
Security with Row Level Security
Row Level Security (RLS) provides fine-grained control over which rows users can access. Rather than handling all authorization in application code, RLS policies define rules that PostgreSQL enforces at the database level.
How RLS Works
When you enable RLS on a table, all access passes through security policies. Without appropriate policies, users cannot read or modify any rows. Policies define conditions that rows must satisfy for operations to succeed.
RLS Policy Components
| Component | Description |
|---|---|
| Command | Which operations the policy affects (SELECT, INSERT, UPDATE, DELETE) |
| Role | Which roles the policy applies to |
| Using Expression | Conditions for SELECT and UPDATE operations |
| With Check Expression | Conditions for INSERT and UPDATE on new data |
Example RLS Policies
-- Enable RLS on the profiles table
alter table profiles enable row level security;
-- Users can only see their own profile
create policy "Users can view own profile" on profiles
for select using (auth.uid() = user_id);
-- Users can update their own profile
create policy "Users can update own profile" on profiles
for update using (auth.uid() = user_id)
with check (auth.uid() = user_id);
-- Admins can view all profiles
create policy "Admins can view all" on profiles
for select using (auth.role() = 'admin');
Integration with Authentication
Supabase's authentication integrates with RLS through auth.uid() which returns the authenticated user's ID. This creates a straightforward pattern for user-specific data access regardless of how the data is accessed.
Benefits of RLS
- Defense in Depth: Security applies at the database level, not just application level
- Consistent Enforcement: Rules apply across all access methods
- Simpler Code: No need to manually filter queries by user
- Audit Trail: Policies document access rules explicitly
Best Practices for Supabase Database
Schema Design
- Design schemas that accurately represent your domain while anticipating future needs
- Normalize data to reduce redundancy, but don't over-normalize
- Use appropriate data types--avoid strings where integers would be clearer
- Add constraints to protect data integrity at the database level
Migration Discipline
- Keep migrations small and focused on single logical changes
- Write idempotent migrations safe to run multiple times
- Test migrations in staging before production deployment
- Use
IF EXISTSandIF NOT EXISTSclauses appropriately
Performance
- Create indexes strategically to support common query patterns
- Use
EXPLAIN ANALYZEto understand query performance - Monitor queries over time as data grows
- Avoid over-indexing which slows writes and increases storage
Connection Methods
| Method | Best For |
|---|---|
| Auto-generated API | Rapid prototyping, CRUD operations with RLS |
| Direct PostgreSQL | Advanced features, raw SQL, any PostgreSQL client |
| Client Libraries | Type-safe queries, convenient methods |
Security Checklist
- Enable RLS on all tables containing user data
- Create policies for every operation on protected tables
- Use environment variables for sensitive configuration
- Regularly review and audit database access
- Keep PostgreSQL updated for security patches
For local development setup, learn about Supabase Local Development to streamline your development workflow.