feat: init drizzle implementation for backend (express) and frontend (vite-react)

This commit is contained in:
afiqzudinhadi 2025-07-08 16:38:55 +08:00
parent fc3a21263a
commit aca6a4da98
17 changed files with 1608 additions and 35 deletions

View file

@ -0,0 +1,2 @@
# Drizzle ORM configuration for Supabase PostgreSQL database
DATABASE_URL=

View file

@ -0,0 +1,78 @@
import { Router } from "express";
import { getUsers, createUser } from "@repo/db/index.ts";
import { randomUUID } from "crypto";
const router = Router();
// GET /api/users - Get all users
router.get("/", async (req, res) => {
try {
const users = await getUsers();
res.json(users);
} catch (error: any) {
console.error("Error fetching users:", error);
res.status(500).json({
error: "Failed to fetch users",
message: error.message,
});
}
});
// POST /api/users - Create new user
router.post("/", async (req, res) => {
try {
const { name, email, password, role, isActive } = req.body;
// Validation
if (!name || !email || !password) {
return res.status(400).json({
error: "Missing required fields: name, email, and password are required",
});
}
// Email format validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.status(400).json({ error: "Invalid email format" });
}
// Password length validation
if (password.length < 6) {
return res
.status(400)
.json({ error: "Password must be at least 6 characters long" });
}
// Role validation (based on schema enum)
const validRoles = ["admin", "user"];
if (role && !validRoles.includes(role)) {
return res.status(400).json({
error: "Invalid role. Must be 'admin' or 'user'",
});
}
const userData = {
id: randomUUID(),
name: name.trim(),
email: email.toLowerCase().trim(),
role: role || "user", // Default role
};
const newUser = await createUser(userData);
res.status(201).json(newUser);
} catch (error: any) {
console.error("Error creating user:", error);
// Handle unique constraint violation (duplicate email)
if (error.message?.includes("duplicate") || error.code === "23505") {
return res.status(409).json({ error: "Email already exists" });
}
res.status(500).json({
error: "Failed to create user",
message: error.message,
});
}
});
export default router;

View file

@ -2,21 +2,25 @@ import { json, urlencoded } from "body-parser";
import express, { type Express } from "express";
import morgan from "morgan";
import cors from "cors";
import productRoutes from "./routes/products";
import userRoutes from "./routes/users";
export const createServer = (): Express => {
const app = express();
app
.disable("x-powered-by")
.use(morgan("dev"))
.use(urlencoded({ extended: true }))
.use(json())
.use(cors())
.get("/message/:name", (req, res) => {
return res.json({ message: `hello ${req.params.name}` });
})
.get("/status", (_, res) => {
return res.json({ ok: true });
});
const app = express();
app.disable("x-powered-by")
.use(morgan("dev"))
.use(urlencoded({ extended: true }))
.use(json())
.use(cors())
return app;
.get("/message/:name", (req, res) => {
return res.json({ message: `hello ${req.params.name}` });
})
.get("/status", (_, res) => {
return res.json({ ok: true });
})
.use("/api/products", productRoutes)
.use("/api/users", userRoutes);
return app;
};

View file

@ -1,9 +1,9 @@
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"lib": ["ES2015"],
"outDir": "./dist"
},
"exclude": ["node_modules"],
"include": ["."]
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"lib": ["ES2015"],
"outDir": "./dist"
},
"exclude": ["node_modules"],
"include": ["."]
}