Skip to content

SaxonF/supabase-react

Repository files navigation

Supabase React

Type-safe reactive queries for Supabase with real-time subscriptions and optimistic updates.

Quick Start

1. Define your schema

Create a schema file (e.g., lib/db/schema.ts) using the provided column types. This schema will be used to generate your database tables and provide end-to-end type safety for your queries.

import {
  enumType,
  integer,
  pgFunction,
  table,
  text,
  timestamp,
  uuid,
  varchar,
} from "supabase-schema";

// 1. Define Functions
export const updateTimestamp = pgFunction("update_timestamp", {
  returns: "trigger",
  language: "plpgsql",
  definition: `
  BEGIN
    NEW.updated_at = now();
    RETURN NEW;
  END;
  `,
});

// 2. Define Tables with Policies, Indexes, and Triggers
export const goals = table("goals", {
  id: uuid({ primaryKey: true }),
  title: text({ notNull: true }),
  user_id: uuid({ notNull: true }),
  created_at: timestamp({ notNull: true, defaultValue: "now()" }),
  updated_at: timestamp({ notNull: true, defaultValue: "now()" }),
})
  .enableRLS()
  // RLS Policies
  .policy("Users can only see their own goals", {
    for: "select",
    to: ["authenticated"],
    using: "auth.uid() = user_id"
  })
  // Composite Indexes
  .index(["user_id", "created_at"])
  // Triggers
  .trigger("handle_updated_at", {
    when: "BEFORE",
    events: ["UPDATE"],
    call: updateTimestamp
  });

export const todos = table("todos", {
  id: uuid({ primaryKey: true }),
  goal_id: uuid({ notNull: true }).references(() => goals.id),
  title: varchar({ notNull: true }),
  completed: integer({ notNull: true, defaultValue: 0 }),
})
  .enableRLS()
  .policy("Allow all on todos", { for: "all", to: ["authenticated"], using: "true" });

export const schema = {
  goals,
  todos,
  updateTimestamp,
} as const;

export type AppSchema = typeof schema;

Advanced Schema Features

Row Level Security (RLS)

Enable RLS and define granular policies directly in your TypeScript code. Support for using and withCheck clauses allows for complex security logic, including referencing pgFunction definitions.

Functions & Triggers

The pgFunction helper allows you to define PostgreSQL functions directly in your schema. These can then be referenced in table triggers using the .trigger() method, supporting BEFORE, AFTER, and INSTEAD OF timings across all standard DML events (INSERT, UPDATE, DELETE, TRUNCATE).

Composite Indexes

Beyond single-column .index() calls on individual columns, you can define composite indexes and unique constraints on multiple columns using the table-level .index(["col1", "col2"]) and .unique(["col1", "col2"]) methods.

Set the following environment variables in your .env file:

NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
DATABASE_URL=your-postgres-connection-string

2. Wrap your app in Provider

Initialize the SupabaseProvider in your root layout or a dedicated providers component.

// components/providers.tsx
"use client"

import { SupabaseProvider } from "supabase-react"
import { schema } from "@/lib/db/schema"

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <SupabaseProvider
      url={process.env.NEXT_PUBLIC_SUPABASE_URL!}
      publishableKey={process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY!}
      schema={schema}
    >
      {children}
    </SupabaseProvider>
  )
}

3. Use the Hooks

useQuery

Fetch data with automatic real-time updates and relation handling.

const { data, isLoading } = useQuery<AppSchema, "goals">("goals", {
  include: {
    todos: {
      where: { completed: 0 }
    }
  }
});

useMutation

Perform type-safe mutations with optional optimistic updates.

const { insert } = useMutation<AppSchema, "goals">("goals", {
  optimisticUpdate: true
});

const handleAdd = () => {
  insert({ title: "New Goal" });
};

useAuth

Manage authentication state easily.

const { user, signInAnonymously, signOut } = useAuth();

4. Push Schema Changes

When you update your schema.ts file, push the changes to your Supabase database:

npx supabase-schema push ./lib/db/schema.ts

5. Pull Remote Changes

If you've made changes directly via the Supabase dashboard and want to update your local schema:

npx supabase-schema pull

Packages

  • supabase-react: React hooks and provider.
  • supabase-schema: Schema definition and CLI tools.

Releases

No releases published

Packages

No packages published