Backend & Data

PostgreSQL

PostgreSQL (often called "Postgres") is an advanced, enterprise-class database system with over 30 years of active development. Key features include:

PostgreSQL

PostgreSQL is a powerful, open-source relational database management system that forms the foundation of Supabase.

Introduction to PostgreSQL

PostgreSQL (often called "Postgres") is an advanced, enterprise-class database system with over 30 years of active development. Key features include:

  • ACID Compliance: Ensuring data validity despite errors or failures
  • Advanced Data Types: Including JSON, arrays, hstore, and geometric types
  • Extensibility: Custom functions, operators, data types, and more
  • Concurrent Support: Multi-version concurrency control (MVCC)
  • Full-Text Search: Built-in indexing and searching capabilities
  • Advanced Indexing: B-tree, Hash, GiST, SP-GiST, GIN, and BRIN

Connection Through Supabase

In VibeReference, you'll typically interact with PostgreSQL through Supabase, which provides a user-friendly interface and API for database operations:

// lib/supabase/client.ts
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

// Query example
const { data, error } = await supabase
  .from('your_table')
  .select('*')
  .eq('status', 'active');

Schema Design

Basic Schema Example

Here's a simple schema example for a VibeReference application:

-- Users table (extends Supabase auth.users)
CREATE TABLE public.profiles (
  id UUID REFERENCES auth.users(id) PRIMARY KEY,
  display_name TEXT,
  avatar_url TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

-- Projects table
CREATE TABLE public.projects (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  name TEXT NOT NULL,
  description TEXT,
  owner_id UUID REFERENCES public.profiles(id) NOT NULL,
  is_public BOOLEAN DEFAULT false,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

-- Project members junction table
CREATE TABLE public.project_members (
  project_id UUID REFERENCES public.projects(id) ON DELETE CASCADE,
  user_id UUID REFERENCES public.profiles(id) ON DELETE CASCADE,
  role TEXT NOT NULL CHECK (role IN ('admin', 'editor', 'viewer')),
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
  PRIMARY KEY (project_id, user_id)
);

Data Types

PostgreSQL supports a rich set of data types:

Data Type Description Example
TEXT Variable length text 'Hello, world!'
VARCHAR(n) Variable length with limit VARCHAR(100)
INTEGER 4-byte integer 42
BIGINT 8-byte integer 9223372036854775807
NUMERIC Exact decimal number NUMERIC(10,2) for currency
BOOLEAN True/false value TRUE, FALSE
UUID Universal unique identifier uuid_generate_v4()
TIMESTAMP Date and time TIMESTAMP WITH TIME ZONE
JSONB Binary JSON data '{"key": "value"}'
ARRAY Array of values INTEGER[], TEXT[]

Row Level Security (RLS)

PostgreSQL's Row Level Security feature is a cornerstone of securing your VibeReference application. RLS allows you to define policies that restrict which rows a user can access.

Example RLS Policy

-- Enable RLS on the projects table
ALTER TABLE public.projects ENABLE ROW LEVEL SECURITY;

-- Create policy for viewing projects
CREATE POLICY view_projects ON public.projects
  FOR SELECT
  USING (
    is_public OR                                  -- Public projects are visible to all
    owner_id = auth.uid() OR                      -- Owner can see their projects
    EXISTS (                                       -- Members can see their projects
      SELECT 1 FROM public.project_members
      WHERE project_id = projects.id AND user_id = auth.uid()
    )
  );

-- Create policy for updating projects
CREATE POLICY update_projects ON public.projects
  FOR UPDATE
  USING (
    owner_id = auth.uid() OR                      -- Owner can update
    EXISTS (                                       -- Admins and editors can update
      SELECT 1 FROM public.project_members
      WHERE project_id = projects.id 
      AND user_id = auth.uid() 
      AND role IN ('admin', 'editor')
    )
  );

Common PostgreSQL Operations

SELECT Queries

-- Basic select
SELECT * FROM projects WHERE is_public = true;

-- Joins
SELECT p.name, pr.display_name as owner
FROM projects p
JOIN profiles pr ON p.owner_id = pr.id;

-- Aggregation
SELECT COUNT(*), owner_id 
FROM projects 
GROUP BY owner_id
HAVING COUNT(*) > 5;

-- Window functions
SELECT 
  p.name, 
  p.created_at,
  ROW_NUMBER() OVER(PARTITION BY p.owner_id ORDER BY p.created_at DESC) as row_num
FROM projects p;

Data Manipulation

-- Insert
INSERT INTO projects (name, description, owner_id, is_public)
VALUES ('New Project', 'Description here', 'user-uuid', true);

-- Update
UPDATE projects 
SET name = 'Updated Name', updated_at = now()
WHERE id = 'project-uuid';

-- Delete
DELETE FROM projects WHERE id = 'project-uuid';

Using JSON

-- Query JSON data
SELECT data->>'name' as name
FROM resources
WHERE data->>'type' = 'document';

-- Update JSON data
UPDATE resources 
SET data = jsonb_set(data, '{status}', '"archived"')
WHERE id = 'resource-uuid';

Real-time Features with Supabase

PostgreSQL's LISTEN/NOTIFY feature powers Supabase's real-time capabilities:

// Subscribe to changes on the projects table
const channel = supabase
  .channel('schema-db-changes')
  .on(
    'postgres_changes',
    {
      event: '*', // Listen to all changes
      schema: 'public',
      table: 'projects',
    },
    (payload) => {
      console.log('Change received!', payload);
      // Update UI or state based on the change
    }
  )
  .subscribe();

Migrations and Schema Management

For VibeReference projects, it's recommended to manage your PostgreSQL schema using migration files:

-- migrations/001_initial_schema.sql
CREATE TABLE public.profiles (
  id UUID REFERENCES auth.users(id) PRIMARY KEY,
  display_name TEXT,
  avatar_url TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);

-- More table definitions...

-- Apply migrations from your application or using Supabase migrations

Performance Optimization

Indexing

-- B-tree index (default, good for equality and range queries)
CREATE INDEX idx_projects_owner ON projects(owner_id);

-- Unique index
CREATE UNIQUE INDEX idx_projects_name_owner ON projects(name, owner_id);

-- Partial index
CREATE INDEX idx_active_projects ON projects(created_at) 
WHERE is_archived = false;

-- Text search index
CREATE INDEX idx_projects_description_gin ON projects 
USING GIN (to_tsvector('english', description));

Query Optimization

  • Use EXPLAIN ANALYZE to understand query execution plans
  • Consider denormalization for frequently accessed data
  • Use appropriate indexes for your query patterns
  • Keep statistics up to date with regular ANALYZE

Resources

Ready to build?

Go from idea to launched product in a week with AI-assisted development.