DEV Community

Cover image for Creating New Supabase Users In NextJS
Monica Powell
Monica Powell

Posted on • Originally published at aboutmonica.com

Creating New Supabase Users In NextJS


This article is the beginning of a series about setting up a NextJS with Supabase for user management and database storage. View the next part of this series: creating protected routes with NextJS and Supabase

This article walks through how to create new users for a Supabase database with an API written in NextJS. Note: at the time of this writing that Supabase is free for beta users which is pretty nifty as they include a hosted Postgres database which makes it faster to get an application up and running with a functional database. After the Beta period wraps up Supabase is planning on charging for hosting and will offer current Beta users 1 year of base tier usage for free.

I am currently building a SaaS (Software as a Service) website along with some other Egghead folks who are creating different types of SaaS applications. I am building this app from "scratch" and am currently in the phase of setting up authentication. For this project I am focused on learning new technology and documenting my learnings therefore I decided to try out Supabase, which is an Open Source alternative to Google's Firebase. The specific application I am working towards building is, Shine Docs which will allow folks to document professional achievements in a granular way.

Here's a blurb from the project's README:

[...]your place to shine, showcase and document more granular professional (or otherwise) achievements. Did you fix a gnarly production bug in record time? Did you give your 1st (or 20th) conference talk? Did you write out some solid documentation? Make sure you capture all of this awesomeness in more in a dedicated space. These types of receipts translate well into future resumes, negotiations, annual reviews and reminders to yourself. Inspired by Julia Evan’s article on getting your work recognized with a brag document.

This application is intended for folks looking for more structure with how they document granular professional achievements as well as those looking to have their achievements be more visible.

Set Up NextJS

If you do not yet have a NextJS site you should set up a boilerplate NextJS site as a starting point. This can be done by running the command npx create-next-app to spinup up a default NextJS site. After going through the prompts you should open your newly created directory containing the site.

The next step in setting up NextJS to interact with Supabase is to install Supabase dependencies with @supabase/supabase-js and then run yarn dev to run the site locally. If everything worked out you should be able to visit localhost:3000 and see your Next site running.

Set up Supabase Project

On Supabase we will create a new project and then retrieve the API key and URL from https://app.supabase.io/project/yourprojecturl]/settings/api which can be navigated to by going to your project > settings > API.

Alt Text
a screenshot of the Supabase settings page

In order for our application to be able to interact with our project's DB we will use environment variables to store the necessary values. As a Mac user, I tend to store environment variables in ~/.bash_profile.

You can add the following your ~/.bash_profile or wherever you store local environment variables:

export SUPABASE_KEY="SUPABASEKEYFROMSETTINGSSCREEN"
export SUPABASE_URL="SUPABASEURLFROMSETTINGSSCREEN"
Enter fullscreen mode Exit fullscreen mode

If you already have a terminal session running then after you save your environment variables you should run source ~/.bash_profile to ensure that the newly exported environment variables are available to your NextJS app to access.

We will then create a supabaseClient.js file (in utils/) to set up the Supabase client that is used to interact with Supabase DB to use the URL and API key that were set in the previous step.

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

// retrieving environment variables
const supabaseUrl = process.env.SUPABASE_URL
const supabaseKey = process.env.SUPABASE_KEY

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

Having the Supabase client live in a standalone file will be helpful when we have multiple API endpoints that interact with Supabase that require the same credentials.

Registering Supabase User

Now we will call Supabase to register users by creating a new API function inside of pages/api that uses our Supabase client.

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

export default async function registerUser(req, res) {
  // destructure the e-mail and password received in the request body.
  const { email, password } = req.body

  //make a SignUp attempt to Supabase and
  // capture the user (on success) and/or error.

  let { user, error } = await supabase.auth.signUp({
    email: email,
    password: password,
  })
  // Send a 400 response if something went wrong
  if (error) return res.status(401).json({ error: error.message })
  // Send 200 success if there were no errors!
  // and also return a copy of the object we received from Supabase
  return res.status(200).json({ user: user })
}
Enter fullscreen mode Exit fullscreen mode

You can learn more about HTTP status codes on what different status codes mean on my site https://www.httriri.com/.

Now, let's actually use the Supabase endpoint to create users. Our site's visitors will fill out a form to register therefore let's create a form that requires an e-mail and password and calls the previously created Register endpoint upon form submission. This form will then be imported and used in our index file, index.js.

<form onSubmit={registerUser}>
  <label htmlFor="email">Email</label>
  <input
    id="email"
    name="email"
    type="email"
    autoComplete="email"
    required
  />
  <label htmlFor="password">Password</label>

  <input
    type="password"
    id="password"
    name="password"
    required
  />
  <button type="submit">Register</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Now let's define what happens onSubmit when registerUser is called by defining registerUser. This function will receive the email and password typed into the form from the form submission event and will make a post request to the register endpoint.

export default function Form() {
  const registerUser = async event => {
    event.preventDefault() // prevents page from redirecting on form submissiomn

    // call default function in pages/api/register
    // send the email and password from form submission event to that endpoint
    const res = await fetch("/api/register", {
      body: JSON.stringify({
        email: event.target.email.value,
        password: event.target.password.value,
      }),
      headers: {
        "Content-Type": "application/json",
      },
      method: "POST",
    })

    const result = await res.json()
  }

  return (
    <form onSubmit={registerUser}>
      // above form omitted for brevity
    </form>
  )
}
Enter fullscreen mode Exit fullscreen mode

Now let's look at the response that we are getting back from the API request to the register endpoint.If we destructure res.json() like const { user } = await res.json() then we can see the user object for a successful request looks something like

user {
  id: '2de33395-b88b-4004',
  aud: 'authenticated',
  role: 'authenticated',
  email: 'test@example.com',
  confirmation_sent_at: '2021-03-09T12:35:02.895833829Z',
  app_metadata: { provider: 'email' },
  user_metadata: {},
  created_at: '2021-03-09T12:08:46.611304Z',
  updated_at: '2021-03-09T12:35:03.466491Z'
}
Enter fullscreen mode Exit fullscreen mode

If we've received a 200 response (no errors!) and a user object back from our SignUp call to Supabase then we can redirect users to a message prompting them to confirm their e-mail address. We can use the NextJS router to handle this redirect:

import { useRouter } from "next/router"

export default function Form() {
  const router = useRouter()
  const registerUser = async event => {
    event.preventDefault()

    const res = await fetch("/api/register", {
      body: JSON.stringify({
        email: event.target.email.value,
        password: event.target.password.value,
      }),
      headers: {
        "Content-Type": "application/json",
      },
      method: "POST",
    })

    const { user } = await res.json()
    if (user) router.push(`/welcome?email${user.email}`)
  }

  return <form onSubmit={registerUser}>{/*ommitted for brevity*/}</form>
}
Enter fullscreen mode Exit fullscreen mode

Now we are currently redirecting folks to a Welcome page that doesn't exist so let's create a new page page/welcome.js.

import Footer from "../components/footer";
import { useRouter } from "next/router";

export default function Welcome() {
  const router = useRouter();
  const { email } = router.query;
return (
      <main>
        <p>
          Thank you for signing up. Please check your {email} inbox to verify
          your e-mail address!
        </p>
      </main>
      <Footer />

    </div>

);
}
Enter fullscreen mode Exit fullscreen mode

If all went well then if you fill out the form with an email address and password then you should be redirected to the welcome screen, receive a confirmation e-mail from Supabase to the e-mail you submitted and see under the Authentication section of your project at https://app.supabase.io/project/[yourprojecturl]/auth/users that there is a new user in your users table with the email address that you just submitted. By default Supabase is set up to not allow users to log in until they verify their e-mail address, so unless you've changed that setting you should see the column Last Sign In showing the value "Waiting for verification...".

Example Code on GitHub

Checkout out the example code for this article at: https://github.com/M0nica/register-supabase-users-nextjs-example

That's all I have for now! But I am looking forward to sharing more about how I implement Supabase as I continue my app development.

Top comments (1)

Collapse
 
femolacaster profile image
femolacaster

Beautiful tutorial, Monica.
If I want to add more user data such as first name, last name, phone etc. Do I pass it as a metadata to the auth_users table or the auth table can't be edited and I have to create a new table for this? I suspect the latter.