The following are notes from an Egghead course I took.
Create a Supabase Project with a Table and Example Data
- Created a new database on supabase called Chatter
 - In the table editor, create a new table 
messages, and add columns forid,created_at, andcontent.idshould be a uuidcreated_atshould defaultnow()and never be nullcontentis text and should never be null
 
Setting Up a Remix Project
- Create a new remix project
- Choose “Just the basics”
 - Choose Vercel as the service
 
 
npx create-remix chatter
- For the remix project, you can find the main file in 
index.tsx 
Query Supabase Data with Remix Loaders
npm i @supabase/supabase-js- Add supabase env vars to .env, which can be found in the Project Settings > API. Link
 
SUPABASE_URL={url}
SUPABASE_ANON_KEY={anon_key}
- Create a 
utils/supabase.tsfile. CreatecreateClientfunction - A ”!” can be used at the end of a variable so typescript doesn’t give us errors, if we know those will be available at runtime, like env vars
 - Supabase has row-level security enabled, meaning you have to write policies in order for the user to do CRUD operations (
SELECT,INSERT,UPDATE,DELETE, andALL).- We added a policy to allow all users to read.
 
 - Create the loader in the index page, using 
import { useLoaderData } from "@remix-run/react";, which will allow us to query supabase using the utils.supabase.from("messages").select()reminds me a lot like mongodb’s client.
 
Generate TypeScript Type Definitions with the Supabase CLI
- Add Type Safety checks using the Supabase CLI
 
brew install supabase/tap/supabase
# Or call upgrade if you already have it
brew upgrade supabase
- Create an account token
- Then login to CLI tool using that access token
 
 
supabase login
- Generate types based our project for Typescript
 
supabase gen types typescript --project-id akhdfxiwrelzhhhofvly > db_types.ts
- We have to re-run this command every time we have DB updates
 - Now we use the 
db_types.tsinto oursupabase.tsfile by adding a type to thecreateClientfunction - You can infer types by using 
typeofin Typescript. This is useful for showcasing whatdata’s type is in theIndexfunctional component. - To make sure the data is always present, or an empty array rather than of type 
null, we use a null coalescing operator on the original datareturn { messages: data ?? [] }; 
Implement Authentication for Supabase with OAuth and Github
- Enable Github OAuth using Supabase
- In the supabase project, go to Authentication > Providers
 - Choose Github
 
 - In Github, go to Settings, Developer Settings > OAuth Apps
- Create “Chatter”. Copy the Authorization callback URL
 
 - In supabase, enter the Client ID, Client Secret, and the Redirect URL.
- The generated secret in Github goes away after a few minutes, so be quick
 
 - Create the login component in 
components/loginand then add two buttons for logging in and out.- The handlers should be 
supabase.auth.signInWithOAuthandsupabase.auth.signOut 
 - The handlers should be 
 - Add the login component back into the index component.
- You’ll notice a 
ReferenceErrorin thatprocessis not defined because that should only run on the server. - Change the 
supabase.tsfile tosupabase.server.tsfile. This shows that the supabase file should only be rendered on the server. - The 
root.tsxcomponent has anOutletdepending on the route based off theroutesfiles (file-based routing) 
 - You’ll notice a 
 - In the root component, we add the context in 
Outletfor the supabase instance.- This can now be used in the 
loginfile usinguseOutletContext. - Types can be added by exporting it from root.
 type TypedSupabaseClient = SupabaseClient<Database>;
 - This can now be used in the 
 - supabase uses Local Storage to store the OAuth tokens.
- You can also check the users in the supabase project
 
 
Restrict Access to the Messages Table in a Database with Row Level Security (RLS) Policies
- Add a column to our database called 
user_idand add a foreign key to it, withusersand the key beingid. - Disable Allow Nullable by adding the logged in user id to the first two messages. This can be found in the users table.
 - Re-run the db_types script
 
supabase gen types typescript --project-id akhdfxiwrelzhhhofvly > db_types.ts
- Update the policy by changing the target roles to be authenticated.
 - Now only signed in users will be able to view the data.
 
Make Cookies the User Session Single Source of Truth with Supabase Auth Helpers
- Auth tokens by default are stored in the client’s session, not on the server.
- Remix is loading from the server’s session, which is null
 
 npm i @supabase/auth-helpers-remix- We need to change the mechanism for the token to use cookies
- Auth helpers allows us to use 
createServerClientandcreateBrowserClientto create the supabase instance correctly, based if it’s on the client or server.- You need 
requestandresponseadded in thesupabase.server.ts - We need to do the same thing in the loader in 
rootandindex 
 - You need 
 
 - Auth helpers allows us to use 
 
Keep Data in Sync with Mutations Using Active Remix Loader Functions
- There’s no update for pressing the button because the client doesn’t update the information after the initial load.
 - Remix has a revalidation hook.
 - Supabase has a auth state change hook
- Combining these together, on server and client token change (either new token, or no longer has the token), then refetch data from loaders.
 
 
Securely Mutate Supabase Data with Remix Actions
- To create a new message, we add 
Formfrom remix, which has a methodpost.- This is reminiscent of how forms worked alongside the HTML spec before
 
 - An action is created to insert the message, include the response headers from before (passing along the cookie)
 - The message won’t send yet until the supabase policy is set, so we add a policy for 
INSERTand make sure the user is authenticated and their user_id matches the one in supabase. 
Subscribe to Database Changes with Supabase Realtime
- Supabase sends out updates via websockets when there is a change to the database
- It’s called Replication
 
 - We create a new component, 
RealtimeMessagesthat can subscribe to thoseINSERTchanges on all tables- We set messages in a 
useStatehook, and any changes we will change them with auseEffect - A second 
useEffectsubscribes to these supabase changes 
 - We set messages in a 
 
const channel = supabase
  .channel("*")
  .on(
    "postgres_changes",
    { event: "INSERT", schema: "public", table: "messages" },
    (payload) => {
      const newMessage = payload.new as Message;
      if (!messages.find((message) => message.id === newMessage.id)) {
        setMessages([...messages, newMessage]);
      }
    }
  )
  .subscribe();
Deploy a Remix Application to Vercel from a GitHub Repository
- Create a Github repo
- There’s a Github CLI tool, 
gh, that can handle this gh repo create chatter --public
 - There’s a Github CLI tool, 
 - Init the repo, commit, and push
 - Go to Vercel, add the project and env variables
 - Go to Github and add the redirect URL
 - Go to Supabase and add authentication url redirect
 - Final URL