Building a RESTful API with Supabase, Node.js, and Express.js: A Comprehensive Guide
In the ever-evolving landscape of web development, the demand for robust backend solutions has never been higher. Supabase, a dynamic open-source alternative, emerges as a powerful tool for developers seeking simplicity without compromising functionality. In the comprehensive guide, we'll walk through the process of setting up, running and utilizing Supabase with Node.js and Express.js environment to perform CRUD operations.
Setting the Foundation: Node.js and Express.js
Before diving into Supabase, lets establish the groundwork with Node.js and Express.js.
To get started ,we first initialize our Node.js project and install dependencies:
pnpm init
pnpm i express dotenv @supabase/supabase-js
The express
package allows us to build the API framework. dotenv
will be used to hide sensitive credentials in environment variables. And @supabase/supabase-js
is the Supabase client to connect to the database.
The following is the foundational structure that forms the basis of our integration with Supabase:
// Provided Express.js setup code
const express = require('express');
const app = express();
Integrating Supabase: Connecting the Dots
The magic begins when we integrate Supabase into our Node.js and Express.js application. The following code showcases the seamless connection between your application and Supabase using the @supabase/supabase-js
library.
// Supabase integration code
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);
RESTful Endpoints: Unleashing Supabase's Power
Our guide extends beyond basic connectivity to Supabase by implementing RESTful endpoints. From fetching all users to adding, updating, and deleting individual records, these routes leverage Supabase's capabilities while maintaining a clean and efficient code structure.
Create Users:
app.post('/api', async(req, res) => {
try{
const { name } = req.body
const{ data : users, error } = await supabase
.from('users')
.insert(req.body)
if (error) throw error
res.status(201).json(users)
} catch (error) {
console.error(error)
res.status(500).json({error: "An error occured"})
}
})
app.post('/api/:params', async(req, res) => {
try{
const name = req.params.params
const{ data : users, error } = await supabase
.from('users')
.insert({name})
if (error) throw error
res.status(201).json(users)
} catch (error) {
console.error(error)
res.status(500).json({error: "An error occured"})
}
})
Read Users:
app.get('/api', async(req, res) => {
try{
const { name } = req.body
const{ data : users, error } = await supabase
.from('users')
.select()
if (error) throw error
res.json(users)
} catch(error) {
console.error(error)
res.status(500).json({error: "An error occurred"})
}
})
app.get('/api/:params', async (req, res) => {
try {
const param = req.params.params
if (Number.isInteger(parseInt(param))) {
const userId = parseInt(param)
const { data: users, error } = await supabase
.from('users')
.select('*')
.eq('id', userId)
.single();
if (error) {
throw new Error(error.message);
}
if (users) {
res.status(200).json(users);
} else {
res.status(404).json({ error: 'User not found' });
}
} else {
const paramName = param
const { data: users, error } = await supabase
.from('users')
.select('*')
.eq('name', paramName)
.single();
if (error) {
throw new Error(error.message);
}
if (users) {
res.status(200).json(users);
} else {
res.status(404).json({ error: 'User not found' });
}
}
} catch (error) {
console.error(error);
res.status(500).json({ error: 'An error occurred' });
}
});
Update Users:
app.put('/api/:identifier', async (req, res) => {
try {
const identifier = req.params.identifier
const { name } = req.body
if (!validateName(name)) {
return res.status(400).json({ error : "Invalid name format"})
}
const isNumber = !isNaN(identifier)
const filterKey = isNumber ? 'id' : 'name'
if (filterKey === 'name'){
const { data: user, error } = await supabase
.from('users')
.update({ name })
.eq(filterKey, identifier);
if (error) throw error;
res.json(user);
} else {
const { data: user, error } = await supabase
.from('users')
.update({ name })
.eq(filterKey, identifier);
if (error) throw error;
res.json(user);
}
} catch (error) {
console.error(error);
res.status(500).json({ error: 'An error occurred' });
}
})
Delete User:
app.delete('/api/:identifier', async (req, res) => {
try {
const identifier = req.params.identifier;
if (isNaN(identifier)) {
// Identifier is a name
const filterKey = 'name';
const { error } = await supabase
.from('users')
.delete()
.eq(filterKey, identifier);
if (error) {
throw error;
}
} else {
// Identifier is an ID
const filterKey = 'id';
const { error } = await supabase
.from('users')
.delete()
.eq(filterKey, identifier);
if (error) {
throw error;
}
}
res.sendStatus(204);
} catch (error) {
console.error(error);
res.status(500).json({ error: 'An error occurred' });
}
})
Starting your Server
Finally start the server on a port:
const port = process.env.PORT || 3001
app.listen(port, () => {
console.log(`Server running on port ${port}`);
})
Conclusion: Empowering Your Backend with Supabase
Congratulations! You've successfully set up a robust backend using Supabase, Node.js, and Express.js. This guide has laid the foundation for a scalable and efficient RESTful API, allowing you to harness the full power of Supabase in your web development projects.
Whether you're a seasoned developer or just getting started, integrating Supabase into your tech stack provides a potent solution for handling data with ease. As you explore further, consider expanding your API, adding authentication, or incorporating additional Supabase features to elevate your web development experience.