Introduction to Nuxt 3 & Supabase Auth
A few weeks ago I started working on my first SAS product which is going to be a challenge platform for developers on the front end. I'm using Nuxt 3 and for the back end to keep things simple for myself I opted to use Supabase. For those unaware, Supabase is an open source back-end as a service that provides you with a set of tools to build scalable applications. It's been a breeze to work with and as a front-end developer, it's helped me be able to get my product's back end built quickly and allow me more time to focus on the front end.
Now often looking to build an application, authentication can feel quite daunting for developers. However, with Supabase, it's super easy and can be actually set up within your application in only a matter of minutes. So in this video, I'm going to show you how I implemented Supabase authentication into my SAS product using Nuxt 3 and this includes signing up a user, signing in and signing out a user, detecting if a user is signed in, and lastly protecting authenticator routes using some custom middleware.
Setting Up the Nuxt 3 Application
To get started we're going to create a new Nuxt 3 application. Once that's finished, we'll want to cd into this new folder and install the project dependencies. And for this video, I'm also going to be using the Tailwind CSS module.
Creating and Connecting the Supabase Project
With the Nuxt 3 application setup, let's create a new Supabase project. If you don't have an account, you can create one or you can opt to use your GitHub account. Now once you create your account or signed in, we'll start by creating a new project. We're just going to call this Nuxt 3 + auth. And then for the database password, we'll use the option to generate a password for us. We're going to leave the region as a pre-selected option and keep it on the free plan and click on create new project. Once that's set up, we'll be taken back to the dashboard for our Supabase project.
So back here inside of our Nuxt 3 application, we'll need to connect the Supabase project to our application. And luckily Nuxt has a module that makes this integration extremely easy.
Configuring the Nuxt Supabase Module
First, we're going to need to install this module into our application. Once that's complete, we need to add this module to the Nuxt config and lastly we're going to need to create two environment variables. And since we don't have an env file inside of our project we can easily create one by using the command touch .env inside of the integrated terminal. Then inside of this env file we need to add our two environment variables, one called superbase_URL and the other one called Super Bass_key.
Adding Supabase Credentials
For the value of these variables we can head back over to our Supabase project and we want to click on the settings gear inside of the navigation. We're going to be using this project URL for the Super Bass_URL variable, and then the first value within the project API key section for the Super Bass_key variable. And now that we have that added, if you do have your Nuxt server running you're going to want to restart that and then Supabase will have been integrated and ready to use inside of our Nuxt application.
Implementing User Signup
Now to speed things up I went ahead and created a few pages that we're going to be using here within this video. So let's start by looking at how we can register or create a user.
Now in this file, I went ahead and created a few variables. The first two are going to be to capture the input from our inputs for the email and password, and then these bottom two are going to be for handling invalid and successful attempts to Supabase. And we also have an empty function called signUp which we're going to be using to make our request to Supabase. And this function is going to be executed every time that we click submit within our form.
Now the first thing we need to do to sign up a user is we need to gain access to the Supabase client within this file. And to do this, we can actually use a composable that's available to us in this module called useSupabaseAuthClient. And what this composable does is it gives us access to all the available methods from Supabase to handle authentication. And we're going to store this composable in a new variable called client.
So inside of this function, we're going to create what is called a try-catch block. Inside of the try block, we're going to reference our client variable, which is set equal to this composable called useSupabaseAuthClient. And on here we have access to a property called auth. And on this auth property, we have available to us a few methods, and the method we're going to be using is called signUp. Now this method accepts an object with a few properties for signing up our users. So the properties it accepts is going to be the email which we'll set equal to our email variable and then it accepts a password property which we're going to set equal to our password variable.
Now once this function executes, it's going to return back data and then errors if any are present. And if you wanted to access that data or the errors, what we could do is set this function equal to a new variable and then reference it accordingly. However, to make things a little bit cleaner, we can actually destructure the response that we get back from Supabase. So what we can say is we can create a new variable and then we can destructure the data and the errors from the response. Now since this function will take some time to execute, we're going to want to wait for it to finish before continuing on. Now since we're using the async keyword here in front of our function, we can just use the await keyword in front of here to wait for this function to finish before continuing on.
Handling Signup Errors and Success
So after this function finishes, we're going to want to check to see if we have any errors being returned from Supabase. So what we'll do is an if check to see if we have any errors being returned from Supabase. So we're going to say if we have an error, then we want to throw that error and then it's going to be caught here inside of this try-catch block. And what we want to do is, we're going to then take our errorMessage variable and we're going to set it equal to the error.message being returned from Supabase. Now if we don't have any errors being returned from Supabase, then what we want to do is we want to set our success message variable here which is going to say 'Check your email to confirm your account'.
And one thing we do need to fix here so when we destructured our response from Supabase it should not be errors it should just be error. So this is actually all the functionality that we're going to need in order to sign up a user from our application to Supabase. So let's test this out. So we have the application running here on Port 3000, so let's give this a go. So let's just put an email here, we'll say test.yahoo.com and if we forget to put the password in here and we click on register, then as you can see we're going to get spit out a message here from Supabase saying 'signup requires a valid password'. Then for example if we leave out the email, we just put in a password, then we should get back a different response and you can see it's going to say 'to sign up please provide your email'.
So I do think this is pretty cool that Supabase provides us with this information so that way we can properly inform the users of what exactly the issue is. And the best part about this is we don't have to worry about writing any of this logic. We can just grab it from Supabase and we can display it accordingly here inside of our UI.
Testing the Signup Flow
So now that we've seen that, let's actually create our account. So we'll use my email of [email protected] and we'll just give ourselves a password here and then let's just click on register. And as you can see, we are able to successfully go ahead and create our account. We got our success message but one issue that we do have is that we still see our error message. So what we could do is every time that we run our function we could clear out the error and also success message. And on the network tab for our successful signup function, this is what Supabase went ahead and returned to us. And back here inside of our Supabase project underneath the authentication tab, we can see that newly registered user that we just went ahead and created.
Implementing User Sign-In
So now that we have the ability to register a user, let's see how we can log in a user. And for this, I already have a page created called 'Login' and we have a few things already defined in here. So we have this variable called router which we're going to be using to redirect the user once they have logged in. Then we have these two variables here, one for the email and then one for the password. And then we have this variable for our error message to handle any invalid attempts that we may have from Supabase. And lastly, we have a function called signIn which we'll use to make our request, and this function will be executed each time that we hear the submit event on our form.
So again in this file, to get started we're going to need to import the Supabase client. So for this, we're going to use that composable we used in the register file called useSupabaseAuthClient. And we'll set this equal to a new variable called client. And for this function, it's going to look pretty identical to the one we wrote for registering a user. So let's begin by creating a try-catch block and within the try block, we're going to create a new variable and we're going to destructure the response we get back from Supabase. But for the sign-in function, we're only going to be destructuring the error that we get back from Supabase, and we'll set this equal to await, and then we'll reference our client, then we'll use that auth property, and this time we're going to use a method called signInWithPassword. And this method also accepts an object and we're going to pass it two properties: we're going to pass it the email and then we're going to pass the password.
And if we do encounter an error what we're going to do is the same thing. We're going to do an if check to see if the error exists and if it does we're going to throw that error we get back from Supabase and then we'll handle that here inside of our catch block. So within this catch block, we're going to reference our errorMessage.value and then we're going to set it equal to the error.message we get back from Supabase. And lastly, if you don't encounter any errors, then we're going to use our router and then we're going to use the push method and then we're going to direct them to a page I created called 'profile'.
Testing the Sign-In Flow
So back here in our application, let's test this out. So if we attempt to log in with an account that doesn't exist, so we'll just say [email protected] and I'll just do a fake password and if we click sign in, then obviously that account doesn't exist and we're going to get an error message back saying 'Invalid login credentials'.
So now if we use the account that we went ahead and registered with which was this [email protected] and I did go ahead and verify this, and then we enter the password that we did, we should then get redirected to the profile page, which we do.
Detecting the Authenticated User
Okay, so now that we're able to log into our account, we want to be able to detect in the application if a user is currently logged in. So within the profile page, we can use another composable from this module called useSupabaseUser. And what this composable does is it gives us access to the currently logged in user, and if there is no user logged in then this value will return null. So what we'll do is we'll store this composable in a variable called user and to show you what this returns we can log out to the console this user.value.
So if we save this and head over to the application you can see in the console we're going to get all this information about the currently logged in user. And then here inside of the template, what we can do is access this user to display certain information about the current logged in user. So for example, we can display the current email of the logged in user. And here in the application, you can see the email of the currently logged in user.
Implementing User Sign-Out
All right, so now that we have the ability to register a user, log in a user, and detect if a user is currently logged in, we also want to be able to let the user log out of their account. Now to do this the first thing we'll need is our Supabase client. So what we'll do is use our composable of useSupabaseAuthClient and then we'll store it here in a variable called client. And in this function of logOut, what we'll do is we'll define a new try-catch block. And inside of the try block, we're going to create a new variable and destructure the response we get from Supabase. But again this time we're only going to be destructuring the error. And then we'll set this equal to and we'll use a keyword to wait, and then we'll reference our client variable, the auth property, and then a method on here called signOut.
And if we have any errors from Supabase, we'll do the same thing we've done before is we're going to check if that error exists and if it does, we're going to throw that error and then we'll handle it within our catch block. And for this example what we're going to do is just log out to the console the error message that we might get if we do encounter an error. And if we don't encounter any errors and we're able to sign out successfully, then what we're going to do is redirect the user back to the login page.
Protecting Routes with Custom Middleware
And back within the application, if we click on the logout button, we should be logged out and then we'll be redirected back to our login page. Now the last thing I want to cover in this video is protecting your authenticator routes. So what I mean by that is, for example in this demo, we have that profile page that we created, which is essentially only for authenticated users. So if a user is not signed in then we wouldn't want them to be able to hit that page. But currently if we were to go to this page, so if we say /profile, we can obviously go ahead and hit this page but we're not going to be seeing our email being displayed since we don't have a user currently logged in.
Now ideally we want to restrict users from ever being able to reach this page, and we can easily set this up using some custom middleware within Nuxt. And for those unaware of what middleware is, simply, it's going to be some logic that is going to run before reaching that particular route. And what we can do is we can check to see if the user is authenticated or not before reaching this route. And if they are, let them through and if they are not authenticated then we can redirect them back to somewhere else within our application.
So back here inside of our Nuxt application, we first want to do is you want to create a new folder and this will be called middleware. And then within this middleware folder we're going to create a new file and we'll call this auth.js. Now within this file, we're going to start by exporting this helper function that's available to us by Nuxt called defineRouteMiddleware. And this goes ahead and accepts a callback function. And within this function, we first want to get reference to if a user is currently logged in. So to do this, we're going to use our composable of useSupabaseUser and then we'll store that in a variable called user. Then what we'll do is we're going to check to see if this user value is null meaning they're not logged in, and if they're not logged in then what we want to do is we'll say return and then we're going to navigate the user back to the login page.
Applying and Testing Route Protection
And back inside of our profile page, to use the middleware we just created, we first need to use what is called the definePageMeta macro. And then within here, we can then define something called middleware and pass it an array and then we can reference the name that we gave our middleware which was called auth. And now each time that we try to get to this page, this middleware function of auth is going to be run and it's going to check to see if the user is currently logged in. And if they're not logged in, then it's going to redirect them back to the login page.
Now for this to work properly, you're also going to want to stop and then restart your Nuxt server. So we can just hit Ctrl+C here to stop this and then we can rerun it with npm run dev. And now back within the application, if we attempt to go to the profile page and we're not logged in, we're just going to get simply redirected back to our login page. All right, so that's going to wrap it up for this video and how to add Supabase authentication into your Nuxt 3 app. So hopefully you found this video useful. If you did, be sure to leave a like on it down below. And if you are new here, be sure to subscribe for more content like this. Thank you guys for watching, I'll see you in the next one, take care.