Hey folks how's it going? Hope you're well and taking care of your loved ones.
I recently explored how to set up a Node Express backend with Supabase and Drizzle ORM using JavaScript. If you're just starting or prefer JavaScript at the backendโthis one's for you ๐๐.
Note: This guide assumes you're familiar with backend and frontend development and some level of full stack work.
๐ ๏ธ Project Setup
0. Supabase Setup
- Create a Supabase account and confirm via email.
- Create an organization (default name comes from your email).
- Choose Personal type and Free plan.
- Name your project (e.g.,
Todo
) and set a strong password (youโll need it). - Choose your region and hit Create Project.
1. Backend Setup ๐
- Create folders:
Todo/backend
andTodo/frontend
. - In terminal:
cd backend
npm init
Fill details or use:
package name: (backend) todo-backend
description: "A simple backend for Supabase"
entry point: (index.js)
keywords: backend, supabase
author: Rishabh Shukla
license: (ISC)
type: (commonjs) module
Install dependencies:
npm install express drizzle-orm cors dotenv postgres uuid @supabase/supabase-js nodemon
Add dev
script in package.json
:
"scripts": {
"dev": "nodemon src/index.js"
}
Setup .env
with DATABASE_URL
From Supabase Dashboard โ Connect โ ORMs โ Drizzle โ copy DATABASE_URL
. Add to .env
:
DATABASE_URL=your_connection_string
CORS_ORIGIN=http://localhost:5173
drizzle.config.js
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/models',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL,
},
});
Folder structure now:
|- backend
|- node_modules
|- .env
|- drizzle.config.js
|- package.json
|- src/
|- frontend
Frontend Setup ๐ฑ
cd frontend
npm create vite@latest .
# Choose React + JavaScript
npm install
npm run dev
๐ง Coding Part
Backend Code
src/index.js
import express from 'express';
import cors from 'cors';
import 'dotenv/config';
import { db } from './db/index.js';
import { todos } from "./models/todo.model.js";
import { v4 as uuidv4 } from 'uuid';
import { eq } from "drizzle-orm";
const app = express();
const port = 3000;
app.use(cors({ origin: process.env.CORS_ORIGIN || "*", credentials: true }));
app.use(express.json());
app.get('/', (req, res) => res.send('Hello World!'));
app.post("/todo", async (req, res) => {
const { todo } = req.body;
if (!todo) return res.status(400).send("Todo is required");
try {
const task = await db.insert(todos).values({
id: uuidv4(),
todo,
created_at: new Date(),
updated_at: new Date(),
}).returning();
res.status(201).json(task);
} catch (err) {
console.log(err);
}
});
app.get("/get/todos", async(req, res) => {
try {
const allTodos = await db.select().from(todos);
res.status(200).json(allTodos);
} catch (err) {
console.log(err);
}
});
app.patch("/todo/:id", async (req, res) => {
const { id } = req.params;
const { todo } = req.body;
try {
await db.update(todos).set({ todo, updated_at: new Date() }).where(eq(todos.id, id));
res.status(200).json("Todo updated successfully!");
} catch (err) {
console.log(err);
}
});
app.delete('/todo/:id', async (req, res) => {
const { id } = req.params;
try {
await db.delete(todos).where(eq(todos.id, id));
res.status(200).json("Todo deleted successfully!");
} catch (err) {
console.log(err);
}
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
src/db/index.js
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import "dotenv/config";
const connectionString = process.env.DATABASE_URL;
const client = postgres(connectionString, { prepare: false });
export const db = drizzle(client);
src/models/todo.model.js
import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core";
export const todos = pgTable("todos", {
id: uuid("id").primaryKey().defaultRandom(),
todo: text("title").notNull().unique(),
created_at: timestamp("created_at").notNull().defaultNow(),
updated_at: timestamp("updated_at").notNull().defaultNow().$onUpdateFn(),
});
Generate SQL and apply in Supabase SQL Editor:
npx drizzle-kit generate
Copy generated SQL โ paste in Supabase SQL editor โ click Run.
Frontend Code
App.jsx
import { useState } from 'react';
import Form from './Form';
import List from './List';
function App() {
const [todo, setTodo] = useState("");
const [todoList, setTodoList] = useState([]);
const [isReadyForUpdate, setIsReadyForUpdate] = useState(false);
const [updatedTodo, setUpdatedTodo] = useState("");
const handleUpdate = (todo) => {
setIsReadyForUpdate(true);
setUpdatedTodo(todo.id);
setTodo(todo.todo);
};
return (
<>
<Form
todo={todo}
setTodo={setTodo}
isReadyForUpdate={isReadyForUpdate}
updatedTodo={updatedTodo}
/>
<hr/>
<List todoList={todoList} setTodoList={setTodoList} handleUpdate={handleUpdate} />
</>
);
}
export default App;
Form.jsx
import axios from 'axios';
const Form = ({ todo, setTodo, isReadyForUpdate, updatedTodo }) => {
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = isReadyForUpdate
? await axios.patch(`http://localhost:3000/todo/${updatedTodo}`, { todo })
: await axios.post("http://localhost:3000/todo", { todo });
console.log(response);
setTodo("");
window.location.reload();
} catch (err) {
console.log(err);
}
};
return (
<form className='form' onSubmit={handleSubmit}>
<label htmlFor='Todo'>Todo</label>
<input
type="text"
placeholder="Todo"
name='Todo'
className='input'
onChange={(e) => setTodo(e.target.value)}
value={todo}
/>
<input type="submit" value={isReadyForUpdate ? "Update" : "Add"} className='add-button' />
</form>
);
};
export default Form;
List.jsx
import axios from "axios";
import { useEffect } from "react";
const List = ({ todoList, setTodoList, handleUpdate }) => {
useEffect(() => {
const getTodos = async () => {
const response = await axios.get("http://localhost:3000/get/todos");
setTodoList(response.data);
};
getTodos();
}, [setTodoList]);
const handleDelete = async (id) => {
await axios.delete(`http://localhost:3000/todo/${id}`);
window.location.reload();
};
return (
<div>
{todoList.map((todo) => (
<div key={todo.id} className="todo-list-container">
<p>{todo.todo}</p>
<div className="button-container">
<button onClick={() => handleUpdate(todo)}>Update</button>
<button onClick={() => handleDelete(todo.id)}>Delete</button>
</div>
</div>
))}
</div>
);
};
export default List;
โ Conclusion
You now have a working full stack Todo App using Supabase, Drizzle ORM, and Express.js, all written in JavaScript!
๐ GitHub Repo
๐ท Instagram
๐ฏ LinkedIn
If you have any problem consider asking in comments. Like, share and follow
Top comments (0)