Supabase Database Basics
What is Supabase?
An open source alternative to Google Firebased
PostgreSQL database with real-time capabilities
Authentication with multiple methods/services
PostgreSQL?
Built on top of Postgres, a very scalable relational database. You can manage your database from the Supabase interface.
Create tables, relationships, etc
Enable & disable extensions
Real-time engine on top of Postgres
User Authentication
Create and manage users from Supabase Through the Providesrs
Access Control
Security and access are managed via Postgres RLS (Row Level Security)
How It Works
A user signs up. Supabase creates a new user in the auth.users table
Supabase returns a new JWT(Jason Web Token), which contains the user's UUID
Every request to your database also sends the JWT
Postgres inspects the JWT to determine the user making the request
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
Step 1: Initialize the Project
1.1 Create a Vite Project
Run the following command to create a new Vite project:
Copy npm create vite@latest supabase-todo --template react
or
Copy // 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:
Install dependencies:
1.2 Install Required Packages
Copy npm install @supabase/supabase-js
npm install @supabase/auth-ui-react @supabase/auth-ui-share
npm install react-router-dom tailwindcss
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
Copy 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
Copy // 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
:
Copy @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:
Copy // 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):
Copy // index.html
...
<head>
<link rel="stylesheet" href="./src/output.css">
</head>
...
Step 2: Set Up Supabase
2.1 Create a Supabase Project
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
Create a file src/supabaseClient.js
:
Copy import { createClient } from "@supabase/supabase-js";
const supabaseUrl = "YOUR_SUPABASE_URL";
const supabaseKey = "YOUR_SUPABASE_ANON_KEY";
export const supabase = createClient(supabaseUrl, supabaseKey);
Copy 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)
Copy // .env file
VITE_SUPABASE_ANON_KEY =
VITE_SUPABSE_URL =
Copy // 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()
)
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
Google Cloud > APIs & Services > OAuth consent screen
Create - Provide App Information, user support email, Developer contact information
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
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) ::
Create src/components/Login.jsx
:
Copy //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
:
Copy // 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
:
Copy 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
:
Copy 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