Supabase Crash Course building a Back end - vonschappler/Ultimate-React GitHub Wiki
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.
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.
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:
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 |
Column name | Data type | Default Value | Primary |
---|---|---|---|
id | int8 | N/A | checked |
created_at | timestamptz | now() | not checked |
fullName | text | null | not checked |
text | null | not checked | |
nationalId | text | null | not checked |
nationality | text | null | not checked |
countryFlag | text | null | not checked |
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 |
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:
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.
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:
- Open the automatically generated API docs
- Select a table API
- Scroll down to "select all rows" section
- Change the code from JavaScript to Bash
- Change the Project API from hidden to anon (public)
- Copy and paste the code provided in a terminal
- 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>"
- 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:
- Select the table you wish everyone to have access
- Click on New Policy
- Select Get Started Quickly
- Select the Enable read access to everyone template
- Click on Use this template
- Click on Review
- 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.
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.
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.