Skip to content

Instantly share code, notes, and snippets.

@horizon0708
Created May 21, 2021 10:01
Show Gist options
  • Save horizon0708/21ad56716b14bac21bf2d1f4ce5bab8d to your computer and use it in GitHub Desktop.
Save horizon0708/21ad56716b14bac21bf2d1f4ce5bab8d to your computer and use it in GitHub Desktop.

A Step-By-Step Guide to Creating a Twitter Clone With React and Supabase — Part 4

Welcome to Part 4 of creating a twitter clone with React and Supabase! If you follow along, by the end of the series, you will have deployed a fully functioning app that lets users:

  1. tweet out what they are thinking,
  2. upload avatars and change their profile,
  3. be notified when there are new tweets, and;
  4. be notified when someone has liked their tweet.

In Part 3, we made things a bit prettier, added a nav bar and an edit profile page.

In Part 4, we will:

  • finish off the edit profile page, letting users change their username and upload an avatar to the Supabase storage.
  • create a tweets and favorites table in Supabase for the users' tweets and display them using react-query library.

Letting users upload their avatar

The changes for this part is in this commit. Sorry its a big commit 🙏 but I'll be including important snippets below. In this section, we'll be using Supabase's handy storage to let users upload their avatar to our website.

Big picture first

  1. We need to create a bucket in Supabase
  2. We need to let users upload the image to the bucket and save the filename to our profiles table.
  3. We need to be able to fetch the image from the bucket and display it using the saved filename.

Creating a bucket in Supabase

If you've been following this guide, you've already done this in Part 1, when we used Supabase's user management starter SQL ! If you haven't, you can just run the below snippet in the SQL query editor.

https://gist.github.com/8573ae6a8ebadcc0b81c02a6092d69a6

You can also easily do this via the UI, by going to the storage section and clicking the 'New bucket' button.

![[p4 - storage ui.png]]

Letting users upload the image to the bucket

Letting the users upload a file to Supabase is super easy. All you need do is: https://gist.github.com/ae02a6beb97dd80c35094c41d3719a5e

The filename can be anything you want. The file to be uploaded can be chosen via user's file browser by using theinput element with type of file . In the git commit, I've customised the input button to look a bit different by putting it inside a Button component.

https://gist.github.com/21cfba8f886ba20f7fe9d4d16acc293a

This looks something like this.

![[p4 - upload avatar.png]]

The onChange callback gets called with the ChangeEvent<HTMLInputElement> whenever the user selects an image. So I've chosen to upload and display the file whenever user selects an image. This logic is extracted out into a hook, useUpload. In the hook, I get the file with event.target.files[0] then send it off to Supabase.

https://gist.github.com/fa2bb660462862f78f4a81360879f639

Key's value is the resulting filename, prefixed with the bucket name. e.g. avatars/my-image.png. I save the value of Key as avatar_url in the profiles table when the user submits the form.

Downloading and Displaying images

There are two ways to show our image to the user.

One way is to give a presigned url to the user. https://gist.github.com/99a0e7ad7dc0b701eecf0fd976d6155e

Another way is to give download the object as a blob, then creating a URL from the blob. I chose this way in my project. https://gist.github.com/0b75e51bc96e4ceefb2fe211b199898e

Setting Cache-Control used to be broken

When I started writing the blog post, there was a bug with supabase where it wouldn't set the Cache-Control headers for S3 objects and defaulting to no-cache value. This meant that big images were not being cached by the browser at all.

![[p4 - multiple tweets.png]]

I've raised a ticket when I've discovered it and this has been fixed as of 1.11.14!

Previous workaround using react-query

When the bug hadn't been fixed yet, I've used react-query's caching feature to cache the image in the app instead. The follow snippet will cache and return whatever the promise (fetchAvatar) have returned for an hour (set via staleTime), if the path matches. If you are interested, you can read more about caching via keys in react-query's docs. https://gist.github.com/0663c107ec15c9ecd0ad7e89be515fe4

As of Supabase version 1.11.14, the browser will respect the cache it for us as Cache-Control will be set properly (by default, it will be max-age=3600).

Letting users tweet

With profiles working, its time for us to move to the main event: tweets! The changes for this part is in this commit.

Making tables for tweets and favorites

I've kept tweets table quite simple. A tweet will has the following fields:

  • id ,Big int, Primary Key (auto-generated)
  • createdAt , timestampz (auto-generated)
  • content , text
  • userId, uuid, Foreign Key for profiles table

I've created a favorites table. A favorite has the following fields

  • id, Big int, Primary Key (auto-generated)
  • inserted_at, timestampz (auto-generated)
  • tweetId, Big int, Foreign Key for tweets table
  • userId, uuid, Foreign Key for profiles table

![[p4 - db relations.png]] made with dbdiagram

I don't have SQL queries because I've made both tables with the UI 😅. I've added some test data via UI too. We now need a way to query the db for display out tweets. The query will get a bit complicated, but Supabase can handle that 💪 with functions.

What does the front-end need?

Let's have a look at a tweet see and what data the front-end needs. ![[p4-tweet.png]]

A tweet needs:

  • the user's avatar
  • the user's name
  • when the tweet was created
  • the tweet's content
  • how many people favorited the tweet
  • (if logged in) whether the user has favorited it

This might look this as a Typescript type https://gist.github.com/f5eec6c4a409ba915577a1844973f248

Getting the tweet author's profile is simple enough, it's just a JOIN. But how do we get an array of users who has favorited the tweet? I've booted up supabase's SQL query editor and started crafting a query. With some help from the Stack Overflow, I got a query working.

https://gist.github.com/a47a31f2ebd8cfe0ad8bbad5f067ad0b

Postgres functions like json_agg and json_build_object are available in Supabase, so using those, the above query gives us a list of tweets with

  • id
  • content
  • createdAt
  • favorited_users, an array of favorited users in JSON
  • tweet_author, the user who wrote the tweet in JSON

![[p4 - query result.png]]

Now that we have a query that works, we need a way to call this query from our React app. Conveniently for us, Supabase lets you call posgres functions from front-end, so let's go make a function.

Creating a postgres function

We now need to wrap the above query in a function. I've added u_id optional parameter to the function. When this is supplied, it will filter the tweets by the user's id. This will be used to display only the user's tweets when their profile page.

https://gist.github.com/5d33c435b124748cac26b22fa6f37cec

Once you successfully create the function, you can see that the docs for our function are automatically generated in the "API" section, under "stored procedures".

![[p4-api-stored-procedures.png]]

Calling these are as simple as https://gist.github.com/524c81956f0055fea3aac51dd93d585b

In my commit, I do a ilttle bit of transformation (like figuring out whether the current user has favorited the tweet) to the response to get the data we need for the front-end . https://gist.github.com/195f9940501be4150e12045f8dcee201

This result is then given to TweetCard component using react-query.

https://gist.github.com/7aef112f62d6060355ed526e187fed8d

Resulting in a list of tweets!

![[p4 - tweet results.png]]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment