Supabase Auth: A Node.js Guide
Hey guys! Today, we're diving deep into how to implement Supabase authentication in your Node.js applications. Supabase is an awesome open-source alternative to Firebase, and it provides a suite of tools that make backend development a breeze. One of its standout features is its authentication service, which supports various login methods, including email/password, social providers (like Google and GitHub), and more. So, let’s get started and see how we can integrate this into our Node.js projects!
What is Supabase?
Before we jump into the code, let's quickly cover what Supabase is all about. Supabase is an open-source Firebase alternative that provides you with all the backend features you need to build a scalable application. This includes a Postgres database, authentication, real-time subscriptions, and storage. Think of it as a backend-as-a-service (BaaS) platform that handles the complexities of server-side development, so you can focus on building a great user experience.
Why Choose Supabase?
- Open Source: You have full control and visibility over your backend.
- Postgres Database: Leveraging the power and reliability of Postgres.
- Real-time Capabilities: Built-in support for real-time data updates.
- Authentication: Easy-to-use authentication with multiple providers.
- Storage: Simple file storage solutions.
Setting Up Your Supabase Project
First things first, you’ll need to create a Supabase project. Head over to Supabase and sign up for an account. Once you’re in, create a new project. You'll be prompted to choose a name, a database password, and a region for your database. After filling out these details, Supabase will start provisioning your project. This usually takes a few minutes, so grab a coffee and hang tight.
Once your project is ready, navigate to the project dashboard. Here, you’ll find all the details you need to connect your Node.js application to your Supabase backend. The two key pieces of information you'll need are:
- Supabase URL: This is the unique URL for your Supabase project.
- Supabase Anon Key: This is the public API key that allows your application to access your Supabase project.
Keep these values handy, as we’ll need them in the next steps.
Setting Up Your Node.js Project
Now that we have our Supabase project ready, let’s set up our Node.js application. If you don't already have a Node.js project, create a new one. Open your terminal and run the following commands:
mkdir supabase-auth-node
cd supabase-auth-node
npm init -y
This will create a new directory for your project, navigate into it, and initialize a new npm project with default settings.
Next, we need to install the Supabase client library for Node.js. Run the following command:
npm install @supabase/supabase-js
This command installs the @supabase/supabase-js package, which provides the necessary functions to interact with your Supabase project from your Node.js application.
To keep our Supabase URL and Anon Key secure, we’ll use environment variables. Install the dotenv package to manage these variables:
npm install dotenv
Create a .env file in the root of your project and add your Supabase URL and Anon Key:
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Replace YOUR_SUPABASE_URL and YOUR_SUPABASE_ANON_KEY with the actual values from your Supabase project dashboard. Remember to keep this file secure and never commit it to version control.
Initializing the Supabase Client
Now that we have our environment variables set up, let’s initialize the Supabase client in our Node.js application. Create a new file, index.js, in the root of your project and add the following code:
// index.js
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
console.log('Supabase client initialized');
This code imports the necessary modules, loads the environment variables, and initializes the Supabase client. Run the script using node index.js. If everything is set up correctly, you should see Supabase client initialized in your console.
Implementing User Registration
With the Supabase client initialized, we can now implement user registration. Let’s create a function to handle user sign-ups. Add the following code to your index.js file:
// index.js
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
async function signUpUser(email, password) {
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
});
if (error) {
console.error('Error signing up:', error.message);
} else {
console.log('User signed up:', data);
}
}
// Example usage:
signUpUser('test@example.com', 'securepassword');
This function takes an email and password as arguments and uses the supabase.auth.signUp method to create a new user. If there’s an error during the sign-up process, it logs the error message. Otherwise, it logs the user data. Replace 'test@example.com' and 'securepassword' with your desired email and password, and run the script to test the sign-up functionality.
Implementing User Sign-In
Next, let’s implement user sign-in. Add the following function to your index.js file:
// index.js
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
async function signUpUser(email, password) {
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
});
if (error) {
console.error('Error signing up:', error.message);
} else {
console.log('User signed up:', data);
}
}
async function signInUser(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({
email: email,
password: password,
});
if (error) {
console.error('Error signing in:', error.message);
} else {
console.log('User signed in:', data);
}
}
// Example usage:
// signUpUser('test@example.com', 'securepassword');
signInUser('test@example.com', 'securepassword');
This function uses the supabase.auth.signInWithPassword method to sign in an existing user. It takes an email and password as arguments and logs the user data upon successful sign-in, or the error message if something goes wrong. Make sure the email and password match the ones you used during sign-up. Comment out the signUpUser call and uncomment the signInUser call to test the sign-in functionality.
Handling User Sessions
Once a user is signed in, you’ll want to manage their session. Supabase handles session management automatically. When a user signs in, Supabase returns a session object that includes an access token. This token is used to authenticate subsequent requests. The @supabase/supabase-js library automatically handles storing and refreshing this token.
To access the current user session, you can use the supabase.auth.getSession() method. Add the following code to your index.js file:
// index.js
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
async function signUpUser(email, password) {
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
});
if (error) {
console.error('Error signing up:', error.message);
} else {
console.log('User signed up:', data);
}
}
async function signInUser(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({
email: email,
password: password,
});
if (error) {
console.error('Error signing in:', error.message);
} else {
console.log('User signed in:', data);
}
}
async function getCurrentSession() {
const { data: { session }, error } = await supabase.auth.getSession()
if (error) {
console.error('Error getting session:', error.message);
} else {
console.log('Current session:', session);
}
}
// Example usage:
// signUpUser('test@example.com', 'securepassword');
// signInUser('test@example.com', 'securepassword');
getCurrentSession();
This function retrieves the current session and logs it to the console. If no user is signed in, the session will be null. Uncomment the getCurrentSession() call to test this functionality.
Implementing User Sign-Out
To allow users to sign out, you can use the supabase.auth.signOut() method. Add the following function to your index.js file:
// index.js
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
async function signUpUser(email, password) {
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
});
if (error) {
console.error('Error signing up:', error.message);
} else {
console.log('User signed up:', data);
}
}
async function signInUser(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({
email: email,
password: password,
});
if (error) {
console.error('Error signing in:', error.message);
} else {
console.log('User signed in:', data);
}
}
async function getCurrentSession() {
const { data: { session }, error } = await supabase.auth.getSession()
if (error) {
console.error('Error getting session:', error.message);
} else {
console.log('Current session:', session);
}
}
async function signOutUser() {
const { error } = await supabase.auth.signOut();
if (error) {
console.error('Error signing out:', error.message);
} else {
console.log('User signed out successfully');
}
}
// Example usage:
// signUpUser('test@example.com', 'securepassword');
// signInUser('test@example.com', 'securepassword');
// getCurrentSession();
signOutUser();
This function signs out the current user and logs a success message. Uncomment the signOutUser() call to test this functionality. After signing out, the session will be cleared, and subsequent calls to getCurrentSession() will return null.
Securing Your API
To secure your API endpoints, you can use the access token from the user’s session to authenticate requests. When a user signs in, Supabase returns a JWT (JSON Web Token) that you can use to verify the user’s identity. You can then use this token to protect your API routes.
Here’s a simple example of how you can protect an API route using the access token:
// index.js
require('dotenv').config();
const { createClient } = require('@supabase/supabase-js');
const express = require('express');
const app = express();
const port = 3000;
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
app.use(express.json());
// Middleware to verify JWT token
const authenticate = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'Unauthorized: No token provided' });
}
const { data: { user }, error } = await supabase.auth.getUser(token);
if (error || !user) {
return res.status(401).json({ message: 'Unauthorized: Invalid token' });
}
req.user = user;
next();
};
// Protected route
app.get('/protected', authenticate, (req, res) => {
res.json({ message: `Hello, ${req.user.email}! This is a protected route.` });
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
In this example, we’re using the express package to create a simple API. The authenticate middleware verifies the JWT token from the Authorization header. If the token is valid, it attaches the user object to the request and calls the next middleware. If the token is invalid or missing, it returns a 401 Unauthorized error. To test this, you’ll need to include the access token in the Authorization header of your request.
Conclusion
And there you have it! You’ve successfully implemented Supabase authentication in your Node.js application. We covered setting up your Supabase project, initializing the Supabase client, implementing user registration and sign-in, handling user sessions, and securing your API. Supabase makes authentication straightforward and provides a solid foundation for building secure and scalable applications. Keep experimenting and exploring the other features Supabase offers to enhance your projects!
Key Takeaways
- Supabase as a Firebase Alternative: Understand why Supabase is a great option for backend services.
- Setting up Authentication: Learn how to configure user authentication with Supabase.
- Securing Your API: Implement JWT-based authentication to protect your API endpoints.
By following this guide, you’re well on your way to building awesome applications with Supabase and Node.js. Happy coding!