Choorai
Lv.3 BaaS

Getting Started with Supabase

With Backend-as-a-Service, you can use a PostgreSQL database and authentication without a separate backend server.

What is Supabase?

  • PostgreSQL-based managed database
  • Auth - Built-in email/social login
  • Row Level Security - Data access control
  • Realtime subscriptions - Automatic notifications on data changes
  • Free tier - 50,000 MAU, 500MB storage

1Create a Supabase Project

  1. Sign up at supabase.com (GitHub login recommended)
  2. Click New project
  3. Enter a project name
  4. Set a database password (save it somewhere safe!)
  5. Select a region: Northeast Asia (Seoul) recommended
  6. Click Create new project

Info

Project creation takes about 1-2 minutes. Once complete, you can find your API keys in the dashboard.

2Create Tables (SQL Editor)

Open the SQL Editor in the dashboard and run the schema below.

SQL Editor
-- Create todos table
create table todos (
  id uuid default gen_random_uuid() primary key,
  user_id uuid references auth.users(id) on delete cascade,
  title text not null,
  completed boolean default false,
  created_at timestamptz default now()
);

-- Enable RLS (Row Level Security)
alter table todos enable row level security;

-- Policy: users can only view their own data
create policy "Users can view own todos"
  on todos for select
  using (auth.uid() = user_id);

-- Policy: users can only insert their own data
create policy "Users can insert own todos"
  on todos for insert
  with check (auth.uid() = user_id);

-- Policy: users can only update their own data
create policy "Users can update own todos"
  on todos for update
  using (auth.uid() = user_id);

-- Policy: users can only delete their own data
create policy "Users can delete own todos"
  on todos for delete
  using (auth.uid() = user_id);

Why RLS matters

Without RLS, all users can access all data. auth.uid() returns the currently logged-in user's ID.

3Client Setup (React)

Install Dependencies

Terminal
npm install @supabase/supabase-js @tanstack/react-query

Environment Variables

.env.local
# .env.local
VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key

Find these in the Supabase dashboard under SettingsAPI.

Supabase Client

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

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

4CRUD Example (React Query + Supabase)

Type Definitions

src/types/todo.ts
// src/types/todo.ts
export interface Todo {
  id: string;
  user_id: string;
  title: string;
  completed: boolean;
  created_at: string;
}

export type CreateTodoInput = Pick<Todo, 'title'>;
export type UpdateTodoInput = Partial<Pick<Todo, 'title' | 'completed'>>;

Hooks (TanStack Query)

src/hooks/useTodos.ts
// src/hooks/useTodos.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { supabase } from '../lib/supabase';
import type { Todo, CreateTodoInput, UpdateTodoInput } from '../types/todo';

// Fetch list
export function useTodos() {
  return useQuery({
    queryKey: ['todos'],
    queryFn: async () => {
      const { data, error } = await supabase
        .from('todos')
        .select('*')
        .order('created_at', { ascending: false });

      if (error) throw error;
      return data as Todo[];
    },
  });
}

// Create
export function useCreateTodo() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (input: CreateTodoInput) => {
      const { data: { user } } = await supabase.auth.getUser();
      if (!user) throw new Error('Not authenticated');

      const { data, error } = await supabase
        .from('todos')
        .insert({ ...input, user_id: user.id })
        .select()
        .single();

      if (error) throw error;
      return data as Todo;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });
}

// Update
export function useUpdateTodo() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async ({ id, ...input }: UpdateTodoInput & { id: string }) => {
      const { data, error } = await supabase
        .from('todos')
        .update(input)
        .eq('id', id)
        .select()
        .single();

      if (error) throw error;
      return data as Todo;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });
}

// Delete
export function useDeleteTodo() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (id: string) => {
      const { error } = await supabase
        .from('todos')
        .delete()
        .eq('id', id);

      if (error) throw error;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
  });
}

5Authentication Example (Optional)

With Supabase Auth, you can easily implement email/password login.

src/hooks/useAuth.ts
// src/hooks/useAuth.ts
import { useState, useEffect } from 'react';
import { supabase } from '../lib/supabase';
import type { User } from '@supabase/supabase-js';

export function useAuth() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Check current session
    supabase.auth.getSession().then(({ data: { session } }) => {
      setUser(session?.user ?? null);
      setLoading(false);
    });

    // Listen for auth state changes
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        setUser(session?.user ?? null);
      }
    );

    return () => subscription.unsubscribe();
  }, []);

  // Sign up
  const signUp = async (email: string, password: string) => {
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) throw error;
  };

  // Sign in
  const signIn = async (email: string, password: string) => {
    const { error } = await supabase.auth.signInWithPassword({ email, password });
    if (error) throw error;
  };

  // Sign out
  const signOut = async () => {
    const { error } = await supabase.auth.signOut();
    if (error) throw error;
  };

  return { user, loading, signUp, signIn, signOut };
}

Social login

Social login with GitHub, Google, Discord, and more is also supported. Configure it in the Supabase dashboard under AuthenticationProviders.

When should you use Supabase?

✅ Recommended

  • Building an MVP quickly
  • Focusing on frontend only without backend development
  • When realtime features are needed
  • When you want auth + DB in one solution

⚠️ Consider carefully

  • Complex business logic requirements
  • Heavy external API integrations
  • Custom backend logic is essential
  • Data structure changes frequently

Next steps

Last updated: February 22, 2026 · Version: v0.0.1

Send Feedback

Opens a new issue page with your message.

Open GitHub Issue