DEV Community

jakobpotosme
jakobpotosme

Posted on • Updated on

Supabase & SWR

Hello everyone!
Today we will incorporate the SWR library to fetch data updates in realtime using React Hooks. While working on a project, I realized that changes made within my database were not reflected on my site unless I preformed a refresh. This bothered me as user's are expecting for changes to be made in real time.

After some researching, I stumbled upon SWR which is a react hook used for data fetching. A short TLDR is that

SWR is a strategy to first return the data from cache , then send a fetch request to revalidate the data, and finally come up with the most recent data

A more comprehensive overview can be found here

SWR seemed to have exactly what I needed so I began implementation.

I will be recreating my particular situation here using dummy data but with the same technologies. (Next Js, Supabase, SWR)

We first need to set up our NextJS application.

npx create-next-app name-of-project
Enter fullscreen mode Exit fullscreen mode

Follow the prompts and once installed simply cd over to the newly created project

Installing Tailwind CSS

We will be adding tailwind css to the application as well in order to aid us with our styling.

Run the following two commands

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

The second command will create the 'tailwind.config.js' file for you, it is a config JS file for Tailwind CSS.

Copy and past the content below which will tell Tailwind CSS which files to watch out for.

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
Enter fullscreen mode Exit fullscreen mode

We then will have to add this to our globals.css file.

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

BOOM! Our Nextjs application is now configured to use TailwindCSS!

Finally, run the following command

npm run dev
Enter fullscreen mode Exit fullscreen mode

this will spin up the Nextjs application on your https://localhost:3000

Our local environment is now ready to go! Time to set up Supabase.

Setting up Supabase

Go to the official Supabase website and sign in.

Click + New Project and then click + New Organization to create your new project team. Then enter a name to create your organization.

Image description

Once your organization is created, we can proceed to create the project.

  • Name: Pick whichever you'd like
  • Database Password: Enter a strong password but save it as you may need it in the future!
  • Region: Choose region closest to you
  • Pricing Plan: Free will suffice, you can always upgrade later.

Image description

Finally hit Create new Project, and there we have our new Supabase project! Lets now create our tables.

Creating Tables

Click the Table Editior from the left panel of the dashboard. Then click Create a new table in the center of the screen.

A side panel will appear and we will fill it out as follows.

  • Name: Pick whichever you'd like
  • Description: Optional
  • Enable Row Level Security (RLS) : We will skip at this time, useful for more complex applications but beyond the scope of this post
  • Enable Realtime: We will skip at this time, this may replace SWR but at this time I have not messed with it enough. (A new blog will come soon once I have a better understanding )

  • id: Unique identifier with a type uuid with the default value of uuid_generate_v4()

  • title: The title of post

  • content: The content of post

Connecting to Supabase

Next step is to connect our application to the supabase client with the use of the supabase-js dependency.

npm install @supabase/supabase-js
Enter fullscreen mode Exit fullscreen mode

We must create a .env file in the root of our project and add the following lines to it.

NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Enter fullscreen mode Exit fullscreen mode

The SUPABASE_URL and SUPABASE_ANON_KEY can be found by following these steps:

  1. Go to the "Settings" section of your project.
  2. Click "API" in the sidebar.
  3. Find your API URL in this page.
  4. Find your "anon" and "service_role" keys on this page.

Now restart the server in order for the changes made in the .env file to reflect properly.

Now that the API credentials are in place, lets create a helper file to initialize the Supabase client. Create a utils folder in the root of your project and within that folder, create a supabaseClient.js file. Paste the following inside.

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey)
Enter fullscreen mode Exit fullscreen mode

Now whenever we need to interact with Supabase, we can simply call upon by importing utils/supabase.js.

Creating first post

Our index page is currently just the default so lets change it up. Copy and paste the code below to have

<div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className='flex items-center justify-center h-screen flex-col'>
        <h1 className="text-3xl font-bold ">
          Create a post!
        </h1>

        <form className=' p-20 flex gap-8'>

          <input type='text' className='shadow p-2 border-b w-full outline-none' placeholder='Type here...'></input>
          <button type='submit' className='bg-blue-300 p-2 rounded' >Submit</button>
        </form>
      </main>
    </div>
Enter fullscreen mode Exit fullscreen mode

Inserting post into Supabase

In order for our post to reach Supabase, there are a few things we need.

  1. Import the supabase client
  2. Adding a title,content state to hold our values.
  3. Adding an onSubmit function to handle that data.

Add the following to your code.

import { supabase } from '../utils/supabaseClient'

...

const [title, setTitle] = useState('')
  const [content,setContent] = useState('')

  const onSubmit = async (e) => {
    e.preventDefault()

    try {
      const {data, error} = await supabase
          .from('Posts')
          .insert([
            {title: title, content: content}
          ])

          if(data){
            console.log('success')
          }

    } catch (error) {
      console.log(error)
    }


    setTitle('')
    setContent('')
  }

...

<form onSubmit={onSubmit} className=' p-20 flex flex-col gap-8'>


          <input type='text' className='shadow p-2 border-b w-full outline-none' 
          placeholder='Title...' onChange={e => setTitle(e.target.value)} value={title} ></input>
          <input type='text' className='shadow p-2 border-b w-full outline-none' 
          placeholder='Content...' onChange={e => setContent(e.target.value)} value={content} ></input>
          <button type='submit' className='bg-blue-300 p-2 rounded' >Submit</button>
        </form>

Enter fullscreen mode Exit fullscreen mode

Fill out and submit the form. Now if everything is working correctly you should see a 'success' message in your console and the data on your Supabase table!

Image description

Image description

Great! Now lets retrieve and display the data within our application.

Retrieving Data from Supabase

Next steps will be creating a components folder at the root of your project. Within that folder, create a Posts.js file.

Copy and paste the code below

import React from 'react'

const Posts = ({title,content}) => {
  return (
    <div className='p-10 bg-zinc-200 rounded w-1/3 mb-10 '>
      <div className='flex flex-col gap-y-5'>
      <h1 className='text-xl font-bold'>{title}</h1>
      <p className='font-semibold'>{content}</p>
      </div>


    </div>
  )
}

export default Posts
Enter fullscreen mode Exit fullscreen mode

This will be our Posts component and we are passing it the title ** and **content props that we will see shortly.

Back in our index.js file, we will be adding a getTableData function that will retrieve all the posts from supabase and storing it within our posts state.

  const [posts, setPosts] = useState() 

  ...

  const getTableData = async () =>{

    try {
      const {data, error} = await supabase
          .from('Posts')
          .select('*')

          if(data){

            setPosts(data)
          }

    } catch (error) {
      console.log(error)
    }
  }

 useEffect(()=>{

    getTableData()
  }, [])

Enter fullscreen mode Exit fullscreen mode

We must invoke this function by calling it within a useEffect hook. This will call the function only once, when the page is first rendered.

After importing the Posts component at the top of the application, add the following code below to your index.js file.

import Posts from '../components/Posts'

...

<div className='pb-20 flex flex-col items-center justify-center '>
        {posts && posts.map(elem =>{
          return(<Posts key={elem.id} title={elem.title} content={elem.content} />)
        })}
 </div>

Enter fullscreen mode Exit fullscreen mode

This will display the data only when the posts state is not null. We are then mapping through the data and passing these props:

  • key : Elements identity or (unique identifier)
  • title : title of post
  • content : content of post

We can now successfully view our data but only if we manually refresh. This is where SWR steps in.

Implementing SWR

Lets begin by installing SWR and refactoring our code a bit.

npm install swr
Enter fullscreen mode Exit fullscreen mode

Now we could leave our code as is and leave everything on client side, however lets take advantage of what makes Nextjs so powerful.
We will move all our api related tasks into the api directory.

Create a new file posts.js and paste the code found below within.

import { supabase } from "../../utils/supabaseClient";

const PostsApi = async (req,res) =>{

    if(req.method === 'GET'){

        const {data,error} = await supabase.from('Posts')
                                    .select('*')

        if(error){
            return res.status(500).json({message: error})
        }

        return res.status(200).json({data})
    }

}
export default PostsApi

Enter fullscreen mode Exit fullscreen mode

Lets go over what is going on so far.

We have imported our supabase client in order to retrieve neccessary data. There is a PostsApi asychronous function taking in a req which is a request being made to the API and res which is a response based off the request made. If a 'GET' request has been made, we will then retrieve the data from Supabase and return it.

Now we can interact with our API by fetching 'api/posts'!

Refactoring index.js

Now that our API is complete, we can now remove our getTableData and useEffect to make use of SWR.
Remove the following code

const [posts, setPosts] = useState()

...

const getTableData = async () =>{

    try {
      const {data, error} = await supabase
          .from('Posts')
          .select('*')

          if(data){

            setPosts(data)
          }

    } catch (error) {
      console.log(error)
    }
  }

  useEffect(()=>{

    getTableData()
  }, [])
Enter fullscreen mode Exit fullscreen mode

Replace it with

import useSWR from 'swr'

const fetcher = (url) => fetch(url, {method: 'GET'}).then(res => res.json());

...

const {data: posts ,error} = useSWR('api/posts', fetcher)
Enter fullscreen mode Exit fullscreen mode

Finally we just need to wrap our application in the context SWRConfig providing our global configuration options. Head over to your _app.js file and add the following

import '../styles/globals.css'
import { SWRConfig } from 'swr'

function MyApp({ Component, pageProps }) {
  return (<SWRConfig
        value={{ 
            refreshInterval: 10000,
          }}
        >
    <Component {...pageProps} />
  </SWRConfig>)
}

export default MyApp
Enter fullscreen mode Exit fullscreen mode

In our configuration, we are simply telling SWR to refresh our data every 10 seconds. There are several other options that are available to use and they can be found here.

All done!

Woohoo! Our application now can successfully insert data and see it in real time how exciting! In summary, we have created a Nextjs / Tailwind CSS / Supabase application accompanied with the SWR library to help with our data fetching. A gif is provided below to see it in action.

Image description

We have only scratched the surface of a full on CRUD application and there is certainly room for improvements but I will leave that up to you to experiment and learn on your own.

I hope you have found this helpful and I hope to see you on my next post. Take care!

Top comments (1)

Collapse
 
tonypee profile image
Tony Polinelli

So it seems that when you submit your post, it would just silently submit, and wait 0-10s for the interval to refresh the post listing?
It seems more like it could do an optimistic update, and invalidate the data