Code With Wolf


How To Use React and Supabase Part 2: Working With The Database

How to Use React + Supabase Pt 2: Working with the Database

This demo will teach you how to connect a React application with Supabase.

If you want to learn more about Supabase in general or how to set up authentication easily using supabase in a react application, read this post

Install an ORM

We will use an Object Relational Mapper (ORM) to interact with our database. We will go with Prisma for this demo since it's highly popular, well maintained, and easy to set up. If you want you can use another ORM since Knex, TypeORM, Sequelize, and others are great too so feel free to follow along with those if you are more familiar with them.

If you are familiar with graphql you will find Prisma a breeze to pick up.

Install Prisma yarn add prisma

Why Are We Using an ORM?

It's possible to do everything we are going to with an ORM also without one. You can write raw SQL scripts or use the Supabase UI to do all of these tasks.

The reason that I am using an ORM for this is so that we can have our SQL interactions coded and saved in source control to make it easier to troubleshoot, scale, and collaborate.

Feel free to write raw SQL, use the SupabaseUI, or a different ORM if you would like, but an ORM makes this much easier.

Connect Prisma to our Supabase DB

Run: npx prisma init

This will create a new file called schema.prisma in a new prisma directory.

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = "<YOUR_DB_CONNECTION_STRING>"
}

Replace this with your connection string for the supabase DB you created in the previous post.

Be careful with these sensitive values. You will want to make sure that they remain secrets, so don't push these to github in a public repo. For this demo we will be hard coding them to just keep things simple.

Your connection string should look like this: postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA

Creating Models

Creating models with Prisma has a bit of a learning curve. Check out the docs as you build out these models.

For now, copy and paste this into the prisma/schema.prisma file.

...

model User {
 id Int @id @default(autoincrement())
 firstName String 
 lastName String
 email String
}

We will create a small Customer Relationship Manager. Here we just have a simple User model to get us started.

Create Migrations

Next we need to create our migrations. This is extremly simple and can be done automatically with Prisma.

Run the command: npx prisma migrate dev --name init

You will see a migrations folder created in the prisma directory. If you navigate to your project in the Supabase UI, you will see the are now tables created! One is named User and was created from our migration. (Another table is _prisma_migrations and is used by Prisma to internally keep track of which migrations have been created or rolledback).

Install Prisma Client

Because we are using Prisma, the next step before we can seed and query our DB is to install the Prisma client.

yarn add @prisma/client

Now we will create a new file in the prisma directory called prismaClient.js with the following code.

const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient()

module.exports =  prisma

Seed DB

Now it's time to seed the database. Let's create a file called seedDb.js in the prisma directory that imports the prismaClient we just created and seeds the DB with some dummy data.

We will use faker to create some fake names for the dummy data.

const prisma = require('./prismaClient.js')
const faker = require('faker')


const users = []

function createUsers(){
    const num = 100;
    let x = 0;

    while(x < 100){
        const user = {
            firstName: faker.name.firstName(),
            lastName: faker.name.lastName(),
            email: faker.internet.email(),
            }
    users.push(user)
    x++
    }
}


async function seedDb(){
 await prisma.user.createMany({data: users})
} 


async function main() {
    createUsers()
    await seedDb()
}


main().catch((e) => {
    throw e
  }).finally(async () => {
    await prisma.$disconnect()
  })

Read From DB with the Supabase JS Client

Now that we can create tables with migrations and write to tables using our ORM, we are ready to get back into our React app and perform CRUD applications on this data.

The migrations and seeding in the previous step give us some data tables and mock data to work with here.

From now on, in our react app, we will be using the supabase client to interact with our database.

First let's create a new folder called components in the src directory.

Then in the src/components directory, we will create a new component called Users.jsx, which will looks like this:

import { useEffect, useState } from "react";
import supabase from "../supabase";

export default function () {
  const [loading, setLoading] = useState(true);
  const [users, setUsers] = useState([]);

  async function getUsers() {
    const { data, error } = await supabase.from("User").select();
    setUsers(u => u= data);
  }

  useEffect(() => {
    setLoading(true);
    getUsers();
    setLoading(false);
  }, []);

  return (
    <>
      <h2>Users</h2>
      {loading ? (
        <p>loading...</p>
      ) : (
        <>
          {users?.length ? (
            <ul>
              {users.map((user) => (
                <li>
                  {user.email} : {user.firstName} {user.lastName}
                </li>
              ))}
            </ul>
          ) : (
            <p>No users currently</p>
          )}
        </>
      )}
    </>
  );
}

In theory, this will print every user's email and name.

We don't want to do that unless the user is logged in, so let's update our App.js file to look like this:

import "./App.css";
import supabase from "./supabase";
import { useState, useEffect } from "react";
import Users from "./components/Users";

function App() {
  const [user, setUser] = useState(null);

  supabase.auth.onAuthStateChange((event, session) => {
    if (session?.user) {
      setUser((u) => (u = session.user));
    }
  });

  async function signInWithGithub() {
    const { user, session, error } = await supabase.auth.signIn({
      provider: "github",
    });
  }

  async function signOut() {
    const { error } = await supabase.auth.signOut();
    setUser((u) => (u = null));
  }

  return (
    <div className="App">
      {!user ? (
        <button onClick={signInWithGithub}>Sign In With Github</button>
      ) : (
        <>
          <button onClick={signOut}>Log Out, {user?.email}</button>
          <Users />
        </>
      )}
    </div>
  );
}

export default App;

Now, the user will be presented with a Log In button if not authenticated, and if they are authenticated we should see a list of all of our user's email and names.

Next Steps

To write, update, get real-time updates, create stored-procedures, use storage and the many more features of Supabase, check out the Supabase JS client docs. They are easy to follow and very helpful.

Hopefully this got you started with Supabase and you can be well on your way to building real-time authenticated web apps quickly without the overhead of managing a DB and API.

If you like this post and it helped you, share it with friends.



© 2022 Code With Wolf