Supabase a project tagged "The open-source Firebase killer" is an open-source alternative to Firebase. A SAS platform from Google. Supabase Like Firebase is a SAS platform. Supabase provides you with a Backend as a service to enable rapid application development by keeping your concerns related to only the front end of your application enabling you to focus fully on delivering a good user experience.
Every Supabase project comes with a full Postgres database, an authentication service already configured for you, a cloud storage API, access to Edge functions, and a vector Database for AI. We can see that the offering from Supabase is compact and similar to what Firebase offers you so in today's article I will show you how to set up and integrate the Supabase authentication into your serverless app. Then we'll look at some of the benefits that come with using this approach and finally, we'll see if this is an approach you should consider.
For the example in this article, I'm going to use a react app generated with Vite, we are going to break our discussions into the following headings;
- Create a React project.
- Supabase Integration
- Pros and Cons of Supabse auth
- Should you consider it?
Create a React Project
The React project we are going to create will be generated with Vite so go ahead and run the following command to generate a new Vite project.
$ npm create vite@latest --template react
Follow the prompts in the command line to generate your app, let's go ahead and add the following components to the react app. Let's create a login page component first.
// login.jsx
import { useState } from 'react';
const Login = () => {
const [email, updateEmail] = useState();
const [password, updatePassword] = useState();
return (
<form>
<div>
<label>Email</label>
<input
value={email}
type="email"
onChange={e => updateEmail(e.target.value)}
placeholder="Johndoe@gmail.com"
/>
</div>
<div>
<label>Password</label>
<input
value={password}
type="password"
onChange={e => updatePassword(e.target.value)}
placeholder="*****"
/>
</div>
<div>
<button>Login</button>
</div>
</form>
);
};
export default Login;
Let's create a second component which will be our sign up page.
// signup.jsx
import { useState } from 'react';
const Signup = () => {
const [email, updateEmail] = useState();
const [password, updatePassword] = useState();
return (
<form>
<h3>Create a new account</h3>
<div>
<label>Email</label>
<input
value={email}
type="email"
onChange={e => updateEmail(e.target.value)}
placeholder="Johndoe@gmail.com"
/>
</div>
<div>
<label>Password</label>
<input
value={password}
type="password"
onChange={e => updatePassword(e.target.value)}
placeholder="*****"
/>
</div>
<div>
<button>Create Account</button>
</div>
</form>
);
};
export default Signup;
Now let's create the last component this will be the dashboard but I'll keep it as simple as possible.
// dashboard.jsx
const Dashboard = () => {
return (
<div>
<h3>Welcome back</h3>
</div>
);
};
export default Dashboard;
Now we have a react project let's go ahead and install react-router-dom with the following command;
$ npm i react-router-dom
Edit the App.jsx
file and paste it into the following content.
import { createBrowserRouter} from "react-router-dom";
import { RouterProvider } from "react-router"
import Dashboard from "./dashboard";
import Login from "./login";
import Signup from "./signup";
const router = createBrowserRouter([
{
path: "/",
element: (<Signup />),
},
{
path: "/login",
element: <Login />
},
{
path: "/dashboard",
element: <Dashboard />
}
]);
const App = () => (
<RouterProvider router={router} />
);
export default App;
That's the template all brushed up not slick but surely gets it done. Now let's set up the Supabase integration.
Supabase Integration
First, we need to install Supabase with the following command, make sure that;
- You have visited the homepage and you have also successfully created an account with Supabase.
- And you have a Supabase project up and running.
$ npm install @supabase/supabase-js
Now create a .env
file where you'll put some of the tokens for your authentication on Supabase Go to your Supabase project dashboard click on setting and select API
Make sure to copy your project URL and your anon key. Open up your .env
file and paste those values in.
VITE_PROJECT_URL=PROJECT_URL
VITE_ANON_KEY = ANON_KEY
Now let's create a module inside the src
folder that will house the helper functions for interacting with the Supabase client. Let's call this file helper.js
. Then let's go ahead and set the function for creating a new user account
// helper.js
const PROJECT_URL = import.meta.env.VITE_PROJECT_URL
const ANON_KEY = import.meta.env.VITE_ANON_KEY
const supabase = createClient(PROJECT_URL, ANON_KEY)
export async function signInWithEmail({email, password}) {
try {
const { data, error } = await supabase.auth.signUp({
email,
password,
})
if (error) throw error
return [null, data]
} catch (error) {
return [error, null]
}
}
We have a function that signs in a user with their email address and password. First we create a new Client object using the createClient() function. The first argument to the createClient() function is the project URL, and the second argument is the anon key. The anon key is a special key that can be used to access public data in a Supabase project.
The function then calls the auth.signUp() method to sign up the user. The auth.signUp() method takes two arguments: the email address and the password. The auth.signUp() method returns an object with two properties: data and error. The data property contains data about the user, and the error property contains any errors that occurred.
The function then checks if there is an error. If there is an error, the function throws the error. Otherwise, the function returns the data about the user.
Let's add two more functions one to log in to an existing user and finally, one to retrieve the user and their associated session from Supabase.
// helper.js continues
export async function login({email, password}) {
try {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) throw error
return [null, data]
} catch (error) {
return [error, null]
}
}
export async function getCurrentUser(access_token) {
try {
const { data } = await supabase.auth.getUser(access_token)
return [null, data.user];
} catch (error) {
console.log(error)
return [error, null]
}
}
Now let's update our components so our App is hooked up to Supabase starting with the signup page
// signup.jsx
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
const Signup = () => {
const navigate = useNavigate ()
const signup = async (e, _user) => {
e.preventDefault()
try {
const [error, data] = await signInWithEmail(_user);
if(error) throw error;
alert('Account created successfully, please verify email')
const {access_token, refresh_token} = data.session
sessionStorage.setItem('access_token', access_token);
sessionStorage.setItem('refresh_token', refresh_token);
navigate('/dashboard')
} catch (error) {
console.log(error)
}
}
const [email, updateEmail] = useState();
const [password, updatePassword] = useState();
return (
<form
onSubmit={e => signup(e, {email, password} )}
>
// continued...
</form>
);
};
The component defines a function called signup(). The signup() function takes two arguments: the event object and an object with the email address and password of the user. The signup() function calls the signInWithEmail() function to sign up the user. If the sign-up is successful, the signup() function alerts the user and redirects them to the dashboard page. If the sign-up is not successful, the signup() function logs the error.
The sessionStorage object is used to store the access token and refresh token that is returned by the signInWithEmail() function. The access token is used to authenticate the user with Supabase, and the refresh token is used to get a new access token if the old one expires. Let's do the same for the login page.
// login.jsx
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
const Login = () => {
const navigate = useNavigate();
const [email, updateEmail] = useState();
const [password, updatePassword] = useState();
const login = async (e, _user) => {
e.preventDefault();
try {
const [error, data] = await SignInWithEmail(_user);
if(error) throw error
alert('Log in successful')
const {access_token, refresh_token} = data.session
sessionStorage.setItem('access_token', access_token);
sessionStorage.setItem('refresh_token', refresh_token);
navigate("/dashboard", { replace: true });
} catch (error) {
if (error.message.includes('Email not confirmed')) alert('Please verify your email')
}
}
return (
<form
onSubmit={e => login(e, { email, password })}
>
// continued...
</form>
);
};
Finally, let's do the same for our dashboard page.
// dashboard.jsx
import { useEffect, useState } from 'react'
const Dashboard = () => {
const [currentUser, updateUser] = useState()
useEffect(() => {
const [error, user] = await getCurrentUser(sessionStorage.getItem('access_token'));
if (error) console.log(error);
if (user) updateUser(user)
}, [])
return (
<div>
<h3>Welcome back</h3>
</div>
);
};
export default Dashboard;
That's our integration all done and set up, Let's consider the pros and cons of using this approach.
Pros of Supabse auth
- It reduces your overall development time and boosts your productivity.
- Their API is simple to interact with and understand. I didn't struggle much with setting this project up as you'd observe.
- They have a free tier so you don't have to worry about paying anything if you're just testing.
Cons of Supabse auth
- You Don't have control over the system and there's always this danger of being locked out.
- Porting your existing users to another platform isn't particularly going to be an easy process you're just locked in.
Should you use it?
If you are trying to create something just for fun or to put on your portfolio without paying a buck then you should try it. If you have the money to spare and you're looking to build a $100M Tech firm then you should also go with Supabase it will save you tons of development time and get more out of your team.
I am a big fan of Firebase and because of that, I was very skeptical about trying and using Supabase but ever since I tested it out I've come to appreciate how good it feels using Supabase. Try it out, leave your thoughts about Supabase, and drop your experience about working with Supabase and I'll see you in the next post.