DEV Community

Cover image for The best platform to build serverless apps on in 2023
Kinanee Samson
Kinanee Samson

Posted on

The best platform to build serverless apps on in 2023

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

Now we have a react project let's go ahead and install react-router-dom with the following command;

$ npm i react-router-dom
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now create a .envfile 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
Enter fullscreen mode Exit fullscreen mode

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]
  }
}
Enter fullscreen mode Exit fullscreen mode

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]
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (12)

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

Supabase as a service is great but their pricing plan makes it so I won't use it.

With the free tier if there is no activity in you database for 7 days they inactivate it, and can only be reactivated if you go into the dashboard and reactivate it.

So for the small mom and pop stores that have to maintain a web presents but might get one or two hits a week this is a serious pain in the arse.

And I can't justify $300/year for something that mainly sets there idle.

But like I said the service itself is great I had no problems with it when I was using it.

Happy Coding

Collapse
 
kalashin1 profile image
Kinanee Samson

Fair enough, I've run into this disabling your app part and it's so annoying. Like I think a request to your app should activate it by default not having to manually activate it.

And if what you're building isn't really big then why spend such amount of money? But then again they are a start-up and they have to make tradeoffs.

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

Not bashing on Supabase, just helping folks make an informed decision about using their service :) If I had the traffic I would be using their service, but sadly I don't.

Happy Coding

Thread Thread
 
kalashin1 profile image
Kinanee Samson

okay, nice one. Hope they keep an eye out for it.

Collapse
 
tsdexter profile image
Thomas Dexter • Edited

Can't you just setup pingdom or similar to hit it once a day?

Collapse
 
renfontes profile image
RenFontes

You should try Pocketbase ๐Ÿ‘€

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

Lolol sqlite on steroids.

Yea going to be checking this one out thanks.

Collapse
 
kalashin1 profile image
Kinanee Samson • Edited

Okay nice suggestion bro, I'm definitely going to try that out...

Collapse
 
anik2069 profile image
Md Anik Islam

Great One. I will try this soon in flutter.

Collapse
 
kalashin1 profile image
Kinanee Samson

Thanks alot, I'm glad you found it useful. You should find it fun using this with flutter.

Collapse
 
machineno15 profile image
Tanvir Shaikh

Does Supabase has database relationships ?

Collapse
 
kalashin1 profile image
Kinanee Samson

I've not really taken my time to explore their database but since it's a Postgres database it should.