Supabase Crash Course building a Back end - vonschappler/Ultimate-React GitHub Wiki

What is Supabase?

Supabase is a service that allows developers to easyly create a back-end with a Postgres database. It automatically creates both the database and API so developers can easy request and receive data from the server.

There is no need to develop a back-end, making this the perfect tool for front-end developers to get everything setup quickly!

To sumarise, Supabase is then more than just an API. It's a solution that includes user authentication and file storage, all accessed from their own API or JavaScript library.


Creating an account on Supabase:

In order to create an account on Supabase, just head to their website and opt-in to sing up using your Github account if you have one, or create a new account using your email.

This will then automatically create a free organization from wich you can create a new project which takes a few minutes to get completed.

Just follow the instructions on the website and make sure to copy any passwords and secret keys, storing them in a safe place.


Thinking about states and tables for a project:

When dealing with large applications, we always think about the state in features categories and pages level instead of thinking about it in the component level.

So, going back to the project in which we are going to use Supabase, we can have the following state domains/slices for the listed features categories:

State slice Category
Bookings Bookings
Dashboard
Check in and out
Cabins Cabins
Guests Guests
Settings App settings
Users Authentication

Those state listed are going to be global remote state, which will be stored within Supabase and managed by React Query , so basicaly, for each of those state, there will be a table on our database, which is the reason why from now own we'll refer to them also as data table.

There is in this table a specific table, though, that needs some special attention - the Bookings table. This table is all about a guest booking a cabin, which means that we need to find a way to connect them.

Because Supabase is powered by Postgres, a SQL (relational) Database, we need to join those tables using something called foreign keys.

This means that inside the Bookings table we need to store two special fields - guestId and cabinId, as a way to create a relationship that tells which guest booked which cabin, making those to fields the foreign keys inside bookings.

So, it's now time to start creating the tables which will link the state slices to the features.

In order to tables in Supabase, within the recently created project, just click on the button Create new Table within the table editor and fill in the required fields.

It's advised to have the option Enable Row Level Security checked and then (using the project developed in this cased as reference) add the following specifications listed below:

Cabins table fields:

Column name Data type Default Value Primary
id int8 N/A checked
created_at timestamptz now() not checked
name text null not checked
maxCapacity int2 null not checked
regularPrice float4 null not checked
discount float4 null not checked
description text null not checked
image text null not checked

Guests table fields:

Column name Data type Default Value Primary
id int8 N/A checked
created_at timestamptz now() not checked
fullName text null not checked
email text null not checked
nationalId text null not checked
nationality text null not checked
countryFlag text null not checked

Settings table fields:

Column name Data type Default Value Primary
id int8 N/A checked
created_at timestamptz now() not checked
minBookingLength int2 null not checked
maxBookingLenth int2 null not checked
maxGuests int2 null not checked
breakfastProce float4 null not checked

Bookings table fields:

Column name Data type Default Value Primary
id int8 N/A checked
created_at timestamptz now() not checked
startDate timestamp null not checked
endDate timestamp null not checked
numNights int2 null not checked
numGuests int2 null not checked
cabinPrice float4 null not checked
extrasPrice float4 null not checked
totalPrice float4 null not checked
status text null not checked
hasBreakfast bool null not checked
isPaid bool null not checked
cabinId int8
(references cabins table id column)*
null not checked
guestId int8
(references guests table id column)**
null not checked

NOTES:

  1. When thinking about relationships, we need to give consideration on how many entities are linked to each other. In the example, one booking can be only about one cabin and one guest at the same time, although a guest can create bookings for multiple cabins.

    This means that the foreing key needs always to be placed on the table that relates to a single row - reason why the cabin and guests are placed inside bookings and not the other way around.

* To create a reference between bookings and cabins, when defining the column we click on the chain button next to the column name and fill in the fields by selecting the cabins table and select the id as the recerence column.

** To create a reference between bookings and guests, when defining the column we click on the chain button next to the column name and fill in the fields by selecting the guests table and select the id as the recerence column.


Adding Security Policies:

The security policies we enabled when creating our tables prevent users from doing any unwanted operations with them. I order to test and check if they are working, what we can do is:

  1. Open the automatically generated API docs
  2. Select a table API
  3. Scroll down to "select all rows" section
  4. Change the code from JavaScript to Bash
  5. Change the Project API from hidden to anon (public)
  6. Copy and paste the code provided in a terminal
  7. Windows users need to change the code a bit to something, making all the code to something like this:
    # the project host, selected table, api key and auth bearer here were ommited for security reasons, but they'll be provided on the copied code
    curl -k "https://<project_host>/rest/v1/<selected_table>?select=*" -H "apikey: <provided_api_key>" -H "Authorization: Bearer <provided_auth_bearer>"
  8. If everything goes as expected, the result of the command will return an empty array ([]).

In order to configure those policies and enable the usage of the tables created, click on the Authentication menu on the side bar, then select Policies.

This will enable us to set the Row-Level-Policies, enabling the use of the API for different API keys and Bearers.

For example, to add read policy to everyone for a specific table, proceed as below:

  1. Select the table you wish everyone to have access
  2. Click on New Policy
  3. Select Get Started Quickly
  4. Select the Enable read access to everyone template
  5. Click on Use this template
  6. Click on Review
  7. Click on Save

If everything was done corectly, the curl command used before should now return the data for table which policies were changed.

All other required policies can be created on the same way, so we are not covering that, as the process will be pretty similar. Just keep in note that those policies need to be set for each created table individually and for each /API key type and operation desired.


Connecting Supabase to a React Application:

The best way to connect Supabase to a React Application is by using the Supabase Javascript library. To make use of this API, just use the following command on your terminal:

npm install @supabase/supabase-js

This will then install the required libraries used to connect React and Supabase.

After intalling that, head to the API documentation and create a new file on your file system, to store the code provided there. The code looks like to something similar to the code below, where we also export the supabase client created with the function createClient for future use on the application:

import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

export default supabase;

With this client, it's possible then to make queries in the database, by using something similar to the code below in our code base:

import supabase from './supabase';

export async function getData() {
  const { data, error } = await supabase.from('tableName').select('*');
  if (error) {
    console.log(error);
    throw new Error('Data could not be loaded');
  }
  return data;
}

It's always possible to read the Supabase Javascript Client documentation for more details.


Adding storage buckets:

Storage buckets allow us to store large files within supabase. In order to create a bucket to your Supabase project, just click on the button storage and then in New Bucket.

From there, it's possible to setup the name for the bucket, accessility rules and other additional settings.

After all settings for the bucket are defined, just click on Save and wait for the bucket to be created.

Adding files to a bucket is as easy as drag and drop, so after the bucket is created, just add files to it, if desired.

⚠️ **GitHub.com Fallback** ⚠️