React Supabase CRUD

Supabase Database Basics

What is Supabase?

https://supabase.io

An open source alternative to Google Firebased

  • PostgreSQL database with real-time capabilities

  • Authentication with multiple methods/services

  • File storage

  • Serverless Functions

PostgreSQL?

Built on top of Postgres, a very scalable relational database. You can manage your database from the Supabase interface.

  • Create tables, relationships, etc

  • Write SQL quries

  • Enable & disable extensions

  • Real-time engine on top of Postgres

  • PostgREST API

User Authentication

Create and manage users from Supabase Through the Providesrs

  • Email/Password

  • <agic Link

  • Google

  • GitHub

  • Facebok

  • More...

Access Control

Security and access are managed via Postgres RLS (Row Level Security)

How It Works

  1. A user signs up. Supabase creates a new user in the auth.users table

  2. Supabase returns a new JWT(Jason Web Token), which contains the user's UUID

  3. Every request to your database also sends the JWT

  4. Postgres inspects the JWT to determine the user making the request

  5. The user's UID can be used in policies to restrict access to rows

Client Libraries

The supabase client makes it easy to access data, authenticate, use storage, etc

Prerequisites

  • Node.js installed

  • A Supabase account

  • Basic knowledge of React

Step 1: Initialize the Project

1.1 Create a Vite Project

Run the following command to create a new Vite project:

npm create vite@latest supabase-todo --template react

or

// Some code
npx create-vite
>Project name: supabase-todo (if you are alredy inside vs code terminal, then just use .)
>Current directory is not empty > Ignore files and continue
>Select  a framework: React
>Variants: JavaScript

Navigate into the project directory:

cd supabase-todo

Install dependencies:

npm install

1.2 Install Required Packages

npm install @supabase/supabase-js 
npm install @supabase/auth-ui-react @supabase/auth-ui-share
npm install react-router-dom tailwindcss

1.3 Configure Tailwind CSS

First, install Tailwind CSS using npm. Open your terminal and run the following commands in your project directory:

Initialize Tailwind CSS: npx tailwindcss init creates a default tailwind. config. js file in your project

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Modify tailwind.config.js to include: Set Up Content Paths in Configuration

Tailwind CSS needs to know where your templates are located to scan them for class names. Edit the tailwind.config.js file to include the paths to all of your HTML and JavaScript files

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

Add Tailwind to src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Compile Your Tailwind CSS (optional)

Now, use the Tailwind CLI to transform the directives into concrete styles based on your configuration and the classes used in your templates.

Execute this command to compile your CSS and watch for any changes:

// Some code
npx tailwindcss -i ./src/index.css -o ./src/output.css --watch

Reference the Compiled CSS in Your HTML

The final step is to link the generated CSS file in your HTML documents. This enables you to start using Tailwind’s utility classes to style your application.

Link to the compiled CSS in your main HTML file (e.g., index.html):

// index.html
...
<head>
   <link rel="stylesheet" href="./src/output.css">
</head>
...

Step 2: Set Up Supabase

2.1 Create a Supabase Project

  • Go to Supabase

  • Sign in and create a new project : Fill in Organization name, project name, generate Database password (Keep it securely), choose region

  • Copy the Project URL and anon key from the Supabase dashboard

2.2 Configure Supabase in React

Create a file src/supabaseClient.js:

import { createClient } from "@supabase/supabase-js";

const supabaseUrl = "YOUR_SUPABASE_URL";
const supabaseKey = "YOUR_SUPABASE_ANON_KEY";
export const supabase = createClient(supabaseUrl, supabaseKey);
  • Go to Supabase site > Dashboard>Home
    Get the following API endpoint from Supabse Dashboard> Project
    YOUR_SUPABASE_URL
    YOUR_SUPABASE_ANON_KEY
  • Use prefix : VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY

Create .env (in root project folder)

// .env file
VITE_SUPABASE_ANON_KEY = 
VITE_SUPABSE_URL = 
// updated supabaseClient.js
import { createClient } from "@supabase/supabase-js";

// Initialize Supabase client with your credentials
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseKey = import.meta.env.VITE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

export default supabase

Step 3: Create the Database Table

Navigate to the Database section in Supabase and create a new table ToDoList with the following columns at Table Editor

  • id: UUID (Primary Key, default gen_random_uuid())

  • name: Text

  • isCompleted: Boolean (default false)

  • user_id: UUID (Foreign Key to auth.users.id)

Enable Row-Level Security (RLS) and add the following policies:

  • Insert Policy: Allow authenticated users to insert their own todos.

  • Select Policy: Allow authenticated users to view only their own todos.

  • Update Policy: Allow authenticated users to update their own todos.

  • Delete Policy: Allow authenticated users to delete their own todos.


Step 4: Implement Authentication in Google Cloud Console

4.1 Create Login Component with SignInGoogle Method

Setup Google Cloud Project

  • Google Cloud > APIs & Services > Credential

  • Create a new project

  • Google Cloud > APIs & Services > OAuth consent screen

    • User Type : Internal

    • Create - Provide App Information, user support email, Developer contact information

    • Save & Continue

  • Back to Dashboard : APIs & Services > Credential > Create Credentials - select OAuth client ID

    • Application type: Web application

    • Name: Web client 1 (default)

    • Authorized redirect URIs : URIs 1 - copy and paste here from supabase >Authendication>Providers > Google and then Create

    • In popup modal, copy Client ID, Client secret

    • enter the callback URL from the Supabase dashboard > Project URL

In Supabase Dashboard, go to Authentication > Providers

  • Click Google icon to expand : Enable Sign in with Google and provide the following info.

    • Client ID (for OAuth) :: from Google APIs & Services > Credential

    • Client Secret (for OAuth) ::

    • Save

Create src/components/Login.jsx:

//Login.jsx component
import { supabase } from "../supabaseClient.js";

const Login = () => {
  const signInWithGoogle = async () => {
    const { user, error } = await supabase.auth.signInWithOAuth({ provider: "google" });
    if (error) console.error("Error logging in", error);
  };

  return (
    <button className="bg-blue-500 text-white p-2 rounded" onClick={signInWithGoogle}>
      Sign in with Google
    </button>
  );
};

export default Login;

Create src/components/Logout.jsx:

// Logout.jsx component
import {supabase} from '../supabaseClient'

const Logout = () => {
  const signOut = async () => {
    const { error } = await supabase.auth.signOut()

    if (error) console.error('Error logging out', error)
  }

  return (
    <button
      className="bg-red-500 text-white p-2 rounded"
      onClick={signOut}>
      Sign out with Google
    </button>
  )
}

export default Logout

4.2 Handle Authentication in App

Modify src/App.js:

import { useState, useEffect } from "react";
import { supabase } from "./supabaseClient";
import Login from "./components/Login";
import TodoList from "./components/TodoList";

function App() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    supabase.auth.getSession().then(({ data }) => {
      setSession(data.session);
    });
    supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
    });
  }, []);

  return <div>{session ? <TodoList /> : <Login />}</div>;
}

export default App;

Step 5: Implement CRUD Operations

5.1 Create Todo Component

Create src/components/TodoList.js:

import { useState, useEffect } from "react";
import { supabase } from "../supabaseClient";

const TodoList = () => {
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState("");

  useEffect(() => {
    fetchTodos();
  }, []);

  const fetchTodos = async () => {
    const { data , error} = await supabase.from("ToDoList").select("*");
    if (error) {
      console.log("Error fetching: ", error)
    } else {
      setTodos(data);
    }
  };

  const addTodo = async () => {
    const newTodoData = {
      name:newTodo,
      isCompleted: false,
      };
    const {data, error} = await supabase.from("ToDoList")
                                        .insert([newTodoData])
                                        .single();
    if(error) {
       console.log("Error adding newToDo: ", error);
     } else {
        fetchTodos();
        setNewTodo("");
    }
  };

  const toggleCompletion = async (id, isCompleted) => {
    await supabase.from("ToDoList").update({ isCompleted: !isCmpleted }).eq("id", id);
    fetchTodos();
  };

  const deleteTodo = async (id) => {
    await supabase.from("ToDoList").delete().eq("id", id);
    fetchTodos();
  };

  return (
    <div>
      <input type="text" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} className="border p-2" />
      <button onClick={addTodo} className="bg-green-500 text-white p-2 ml-2">Add Todo Item</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id} className="flex justify-between p-2">
            <span className={todo.completed ? "line-through" : ""}>{todo.name}</span>
            <div>
              <button onClick={() => toggleCompletion(todo.id, todo.completed)} className="bg-yellow-500 text-white p-1">Complete Task</button>
              <button onClick={() => deleteTodo(todo.id)} className="bg-red-500 text-white p-1 ml-2">Delete Task</button>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

This concludes our guide on setting up a Supabase CRUD Todo List application using React and Vite with Tailwind CSS. 🎉

Result

Last updated