build-node-app-personal 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/build-node-app.js +114 -0
- package/package.json +24 -0
- package/template/.env +3 -0
- package/template/package.json +16 -0
- package/template/server.js +21 -0
- package/template/src/app.js +30 -0
- package/template/src/config/db.js +19 -0
- package/template/src/controllers/userController.js +61 -0
- package/template/src/middleware/errorHandler.js +12 -0
- package/template/src/middleware/logger.js +14 -0
- package/template/src/middleware/notFound.js +8 -0
- package/template/src/middleware/userMiddleware.js +13 -0
- package/template/src/models/userModel.js +24 -0
- package/template/src/routes/userRoutes.js +27 -0
- package/template-mysql/.env +7 -0
- package/template-mysql/package.json +16 -0
- package/template-mysql/server.js +21 -0
- package/template-mysql/src/app.js +28 -0
- package/template-mysql/src/config/db.js +32 -0
- package/template-mysql/src/controllers/userController.js +62 -0
- package/template-mysql/src/middleware/errorHandler.js +12 -0
- package/template-mysql/src/middleware/logger.js +14 -0
- package/template-mysql/src/middleware/notFound.js +8 -0
- package/template-mysql/src/middleware/userMiddleware.js +13 -0
- package/template-mysql/src/models/userModel.js +39 -0
- package/template-mysql/src/routes/userRoutes.js +27 -0
- package/template-none/.env +2 -0
- package/template-none/package.json +15 -0
- package/template-none/server.js +18 -0
- package/template-none/src/app.js +24 -0
- package/template-none/src/config/db.js +5 -0
- package/template-none/src/controllers/userController.js +61 -0
- package/template-none/src/middleware/errorHandler.js +12 -0
- package/template-none/src/middleware/logger.js +14 -0
- package/template-none/src/middleware/notFound.js +8 -0
- package/template-none/src/middleware/userMiddleware.js +13 -0
- package/template-none/src/models/userModel.js +34 -0
- package/template-none/src/routes/userRoutes.js +27 -0
- package/template-postgres/.env +7 -0
- package/template-postgres/package.json +16 -0
- package/template-postgres/server.js +21 -0
- package/template-postgres/src/app.js +28 -0
- package/template-postgres/src/config/db.js +29 -0
- package/template-postgres/src/controllers/userController.js +62 -0
- package/template-postgres/src/middleware/errorHandler.js +12 -0
- package/template-postgres/src/middleware/logger.js +14 -0
- package/template-postgres/src/middleware/notFound.js +8 -0
- package/template-postgres/src/middleware/userMiddleware.js +13 -0
- package/template-postgres/src/models/userModel.js +35 -0
- package/template-postgres/src/routes/userRoutes.js +27 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import url from "url";
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
const __filename = url.fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
function copyTemplate(targetDir, appName, dbType) {
|
|
14
|
+
let templateFolder;
|
|
15
|
+
switch (dbType) {
|
|
16
|
+
case "mongo":
|
|
17
|
+
templateFolder = "template";
|
|
18
|
+
break;
|
|
19
|
+
case "postgres":
|
|
20
|
+
templateFolder = "template-postgres";
|
|
21
|
+
break;
|
|
22
|
+
case "mysql":
|
|
23
|
+
templateFolder = "template-mysql";
|
|
24
|
+
break;
|
|
25
|
+
case "none":
|
|
26
|
+
templateFolder = "template-none";
|
|
27
|
+
break;
|
|
28
|
+
default:
|
|
29
|
+
console.error(
|
|
30
|
+
"Unsupported database type. Use 'mongo', 'postgres', 'mysql', or 'none'."
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const templateDir = path.join(__dirname, "..", templateFolder);
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(templateDir)) {
|
|
37
|
+
console.error("Template directory not found:", templateDir);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
42
|
+
|
|
43
|
+
const entries = fs.readdirSync(templateDir, { withFileTypes: true });
|
|
44
|
+
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const srcPath = path.join(templateDir, entry.name);
|
|
47
|
+
const destPath = path.join(
|
|
48
|
+
targetDir,
|
|
49
|
+
entry.name.replace("APP_NAME", appName)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
copyDirRecursive(srcPath, destPath, appName);
|
|
54
|
+
} else {
|
|
55
|
+
let content = fs.readFileSync(srcPath, "utf8");
|
|
56
|
+
content = content.replace(/APP_NAME/g, appName);
|
|
57
|
+
fs.writeFileSync(destPath, content, "utf8");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function copyDirRecursive(srcDir, destDir, appName) {
|
|
63
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
64
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
65
|
+
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
68
|
+
const destPath = path.join(
|
|
69
|
+
destDir,
|
|
70
|
+
entry.name.replace("APP_NAME", appName)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (entry.isDirectory()) {
|
|
74
|
+
copyDirRecursive(srcPath, destPath, appName);
|
|
75
|
+
} else {
|
|
76
|
+
let content = fs.readFileSync(srcPath, "utf8");
|
|
77
|
+
content = content.replace(/APP_NAME/g, appName);
|
|
78
|
+
fs.writeFileSync(destPath, content, "utf8");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
program
|
|
84
|
+
.name("create-node-app")
|
|
85
|
+
.argument("<app-name>", "name of the new Node.js app")
|
|
86
|
+
.option(
|
|
87
|
+
"-d, --db <type>",
|
|
88
|
+
"database type (mongo, postgres, mysql, none)",
|
|
89
|
+
"mongo"
|
|
90
|
+
)
|
|
91
|
+
.action((appName, options) => {
|
|
92
|
+
const targetDir = path.resolve(process.cwd(), appName);
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(targetDir)) {
|
|
95
|
+
console.error(`Directory ${appName} already exists. Aborting.`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const dbType = String(options.db || "mongo").toLowerCase();
|
|
100
|
+
|
|
101
|
+
console.log(`Creating Node.js app '${appName}'...`);
|
|
102
|
+
console.log(`Database selected: ${dbType}`);
|
|
103
|
+
|
|
104
|
+
copyTemplate(targetDir, appName, dbType);
|
|
105
|
+
|
|
106
|
+
console.log("Project created successfully!");
|
|
107
|
+
console.log(`\nNext steps:`);
|
|
108
|
+
console.log(` cd ${appName}`);
|
|
109
|
+
console.log(` npm install`);
|
|
110
|
+
console.log(` npm run dev`);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
program.parse(process.argv);
|
|
114
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "build-node-app-personal",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI to scaffold a Node.js app with basic setup and DB configuration",
|
|
5
|
+
"bin": {
|
|
6
|
+
"build-node-app": "bin/build-node-app.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "node bin/build-node-app.js",
|
|
10
|
+
"dev": "node bin/build-node-app.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cli",
|
|
14
|
+
"scaffold",
|
|
15
|
+
"node",
|
|
16
|
+
"generator"
|
|
17
|
+
],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"commander": "^12.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/template/.env
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "APP_NAME",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generated Node.js app",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "node server.js",
|
|
8
|
+
"start": "node server.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"dotenv": "^16.4.0",
|
|
13
|
+
"express": "^4.19.0",
|
|
14
|
+
"mongoose": "^8.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { createServer } from "./src/app.js";
|
|
3
|
+
import { connectDB } from "./src/config/db.js";
|
|
4
|
+
|
|
5
|
+
const port = process.env.PORT || 4000;
|
|
6
|
+
|
|
7
|
+
async function start() {
|
|
8
|
+
await connectDB();
|
|
9
|
+
|
|
10
|
+
const app = createServer();
|
|
11
|
+
|
|
12
|
+
app.listen(port, () => {
|
|
13
|
+
console.log(`Server running on http://localhost:${port}`);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
start().catch((err) => {
|
|
18
|
+
console.error("Failed to start server:", err);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { userRouter } from "./routes/userRoutes.js";
|
|
3
|
+
import { requestLogger } from "./middleware/logger.js";
|
|
4
|
+
import { notFound } from "./middleware/notFound.js";
|
|
5
|
+
import { errorHandler } from "./middleware/errorHandler.js";
|
|
6
|
+
|
|
7
|
+
export function createServer() {
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
// Core middlewares
|
|
11
|
+
app.use(express.json());
|
|
12
|
+
app.use(requestLogger);
|
|
13
|
+
|
|
14
|
+
// Health check / root route
|
|
15
|
+
app.get("/", (req, res) => {
|
|
16
|
+
res.json({ message: "Welcome to APP_NAME API" });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// User CRUD routes
|
|
20
|
+
app.use("/api/users", userRouter);
|
|
21
|
+
|
|
22
|
+
// Not found + error handlers
|
|
23
|
+
app.use(notFound);
|
|
24
|
+
app.use(errorHandler);
|
|
25
|
+
|
|
26
|
+
return app;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
export async function connectDB() {
|
|
4
|
+
const uri = process.env.MONGO_URI;
|
|
5
|
+
|
|
6
|
+
if (!uri) {
|
|
7
|
+
console.warn("MONGO_URI not set. Skipping DB connection.");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
await mongoose.connect(uri);
|
|
13
|
+
console.log("Connected to MongoDB");
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error("MongoDB connection error:", err.message);
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { User } from "../models/userModel.js";
|
|
2
|
+
|
|
3
|
+
export async function createUser(req, res, next) {
|
|
4
|
+
try {
|
|
5
|
+
const user = await User.create(req.body);
|
|
6
|
+
res.status(201).json(user);
|
|
7
|
+
} catch (err) {
|
|
8
|
+
next(err);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function getUsers(req, res, next) {
|
|
13
|
+
try {
|
|
14
|
+
const users = await User.find().lean();
|
|
15
|
+
res.json(users);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
next(err);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getUserById(req, res, next) {
|
|
22
|
+
try {
|
|
23
|
+
const user = await User.findById(req.params.id).lean();
|
|
24
|
+
if (!user) {
|
|
25
|
+
return res.status(404).json({ message: "User not found" });
|
|
26
|
+
}
|
|
27
|
+
res.json(user);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
next(err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function updateUser(req, res, next) {
|
|
34
|
+
try {
|
|
35
|
+
const user = await User.findByIdAndUpdate(req.params.id, req.body, {
|
|
36
|
+
new: true,
|
|
37
|
+
runValidators: true,
|
|
38
|
+
}).lean();
|
|
39
|
+
|
|
40
|
+
if (!user) {
|
|
41
|
+
return res.status(404).json({ message: "User not found" });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
res.json(user);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
next(err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function deleteUser(req, res, next) {
|
|
51
|
+
try {
|
|
52
|
+
const user = await User.findByIdAndDelete(req.params.id).lean();
|
|
53
|
+
if (!user) {
|
|
54
|
+
return res.status(404).json({ message: "User not found" });
|
|
55
|
+
}
|
|
56
|
+
res.status(204).send();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
next(err);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Generic error-handling middleware
|
|
2
|
+
export function errorHandler(err, req, res, next) {
|
|
3
|
+
console.error("Error:", err);
|
|
4
|
+
|
|
5
|
+
const status = err.status || 500;
|
|
6
|
+
const message = err.message || "Internal Server Error";
|
|
7
|
+
|
|
8
|
+
res.status(status).json({
|
|
9
|
+
message,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Basic request logger middleware
|
|
2
|
+
export function requestLogger(req, res, next) {
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
|
|
5
|
+
res.on("finish", () => {
|
|
6
|
+
const duration = Date.now() - start;
|
|
7
|
+
console.log(
|
|
8
|
+
`${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
next();
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Basic validation middleware for user payloads
|
|
2
|
+
export function validateUserBody(req, res, next) {
|
|
3
|
+
const { name, email } = req.body || {};
|
|
4
|
+
|
|
5
|
+
if (!name || !email) {
|
|
6
|
+
return res.status(400).json({
|
|
7
|
+
message: "Both 'name' and 'email' are required.",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
next();
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
|
|
3
|
+
const userSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
name: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: true,
|
|
8
|
+
trim: true,
|
|
9
|
+
},
|
|
10
|
+
email: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true,
|
|
13
|
+
trim: true,
|
|
14
|
+
unique: true,
|
|
15
|
+
lowercase: true,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
timestamps: true,
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const User = mongoose.models.User || mongoose.model("User", userSchema);
|
|
24
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import {
|
|
3
|
+
createUser,
|
|
4
|
+
getUsers,
|
|
5
|
+
getUserById,
|
|
6
|
+
updateUser,
|
|
7
|
+
deleteUser,
|
|
8
|
+
} from "../controllers/userController.js";
|
|
9
|
+
import { validateUserBody } from "../middleware/userMiddleware.js";
|
|
10
|
+
|
|
11
|
+
export const userRouter = express.Router();
|
|
12
|
+
|
|
13
|
+
// Create
|
|
14
|
+
userRouter.post("/", validateUserBody, createUser);
|
|
15
|
+
|
|
16
|
+
// Read all
|
|
17
|
+
userRouter.get("/", getUsers);
|
|
18
|
+
|
|
19
|
+
// Read one
|
|
20
|
+
userRouter.get("/:id", getUserById);
|
|
21
|
+
|
|
22
|
+
// Update
|
|
23
|
+
userRouter.put("/:id", validateUserBody, updateUser);
|
|
24
|
+
|
|
25
|
+
// Delete
|
|
26
|
+
userRouter.delete("/:id", deleteUser);
|
|
27
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "APP_NAME",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generated Node.js app (MySQL user CRUD)",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "node server.js",
|
|
8
|
+
"start": "node server.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"dotenv": "^16.4.0",
|
|
13
|
+
"express": "^4.19.0",
|
|
14
|
+
"mysql2": "^3.9.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { createServer } from "./src/app.js";
|
|
3
|
+
import { connectDB } from "./src/config/db.js";
|
|
4
|
+
|
|
5
|
+
const port = process.env.PORT || 4000;
|
|
6
|
+
|
|
7
|
+
async function start() {
|
|
8
|
+
await connectDB();
|
|
9
|
+
|
|
10
|
+
const app = createServer();
|
|
11
|
+
|
|
12
|
+
app.listen(port, () => {
|
|
13
|
+
console.log(`Server running on http://localhost:${port}`);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
start().catch((err) => {
|
|
18
|
+
console.error("Failed to start server:", err);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { userRouter } from "./routes/userRoutes.js";
|
|
3
|
+
import { requestLogger } from "./middleware/logger.js";
|
|
4
|
+
import { notFound } from "./middleware/notFound.js";
|
|
5
|
+
import { errorHandler } from "./middleware/errorHandler.js";
|
|
6
|
+
|
|
7
|
+
export function createServer() {
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
// Core middlewares
|
|
11
|
+
app.use(express.json());
|
|
12
|
+
app.use(requestLogger);
|
|
13
|
+
|
|
14
|
+
// Health check / root route
|
|
15
|
+
app.get("/", (req, res) => {
|
|
16
|
+
res.json({ message: "Welcome to APP_NAME API (MySQL)" });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// User CRUD routes
|
|
20
|
+
app.use("/api/users", userRouter);
|
|
21
|
+
|
|
22
|
+
// Not found + error handlers
|
|
23
|
+
app.use(notFound);
|
|
24
|
+
app.use(errorHandler);
|
|
25
|
+
|
|
26
|
+
return app;
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import mysql from "mysql2/promise";
|
|
2
|
+
|
|
3
|
+
export let pool;
|
|
4
|
+
|
|
5
|
+
export async function connectDB() {
|
|
6
|
+
try {
|
|
7
|
+
pool = await mysql.createPool({
|
|
8
|
+
host: process.env.MYSQL_HOST || "localhost",
|
|
9
|
+
port: Number(process.env.MYSQL_PORT) || 3306,
|
|
10
|
+
database: process.env.MYSQL_DATABASE || "APP_NAME",
|
|
11
|
+
user: process.env.MYSQL_USER || "root",
|
|
12
|
+
password: process.env.MYSQL_PASSWORD || "password",
|
|
13
|
+
waitForConnections: true,
|
|
14
|
+
connectionLimit: 10,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await pool.query("SELECT 1");
|
|
18
|
+
await pool.query(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
20
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
21
|
+
name VARCHAR(255) NOT NULL,
|
|
22
|
+
email VARCHAR(255) NOT NULL UNIQUE
|
|
23
|
+
);
|
|
24
|
+
`);
|
|
25
|
+
|
|
26
|
+
console.log("Connected to MySQL");
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error("MySQL connection error:", err.message);
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createUser as createUserModel,
|
|
3
|
+
getAllUsers,
|
|
4
|
+
getUserById as getUserByIdModel,
|
|
5
|
+
updateUser as updateUserModel,
|
|
6
|
+
deleteUser as deleteUserModel,
|
|
7
|
+
} from "../models/userModel.js";
|
|
8
|
+
|
|
9
|
+
export async function createUser(req, res, next) {
|
|
10
|
+
try {
|
|
11
|
+
const user = await createUserModel(req.body);
|
|
12
|
+
res.status(201).json(user);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
next(err);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getUsers(req, res, next) {
|
|
19
|
+
try {
|
|
20
|
+
const users = await getAllUsers();
|
|
21
|
+
res.json(users);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
next(err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function getUserById(req, res, next) {
|
|
28
|
+
try {
|
|
29
|
+
const user = await getUserByIdModel(req.params.id);
|
|
30
|
+
if (!user) {
|
|
31
|
+
return res.status(404).json({ message: "User not found" });
|
|
32
|
+
}
|
|
33
|
+
res.json(user);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
next(err);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function updateUser(req, res, next) {
|
|
40
|
+
try {
|
|
41
|
+
const user = await updateUserModel(req.params.id, req.body);
|
|
42
|
+
if (!user) {
|
|
43
|
+
return res.status(404).json({ message: "User not found" });
|
|
44
|
+
}
|
|
45
|
+
res.json(user);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
next(err);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function deleteUser(req, res, next) {
|
|
52
|
+
try {
|
|
53
|
+
const success = await deleteUserModel(req.params.id);
|
|
54
|
+
if (!success) {
|
|
55
|
+
return res.status(404).json({ message: "User not found" });
|
|
56
|
+
}
|
|
57
|
+
res.status(204).send();
|
|
58
|
+
} catch (err) {
|
|
59
|
+
next(err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Generic error-handling middleware
|
|
2
|
+
export function errorHandler(err, req, res, next) {
|
|
3
|
+
console.error("Error:", err);
|
|
4
|
+
|
|
5
|
+
const status = err.status || 500;
|
|
6
|
+
const message = err.message || "Internal Server Error";
|
|
7
|
+
|
|
8
|
+
res.status(status).json({
|
|
9
|
+
message,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Basic request logger middleware
|
|
2
|
+
export function requestLogger(req, res, next) {
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
|
|
5
|
+
res.on("finish", () => {
|
|
6
|
+
const duration = Date.now() - start;
|
|
7
|
+
console.log(
|
|
8
|
+
`${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
next();
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Basic validation middleware for user payloads
|
|
2
|
+
export function validateUserBody(req, res, next) {
|
|
3
|
+
const { name, email } = req.body || {};
|
|
4
|
+
|
|
5
|
+
if (!name || !email) {
|
|
6
|
+
return res.status(400).json({
|
|
7
|
+
message: "Both 'name' and 'email' are required.",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
next();
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { pool } from "../config/db.js";
|
|
2
|
+
|
|
3
|
+
export async function createUser(data) {
|
|
4
|
+
const { name, email } = data;
|
|
5
|
+
const [result] = await pool.query(
|
|
6
|
+
"INSERT INTO users (name, email) VALUES (?, ?)",
|
|
7
|
+
[name, email]
|
|
8
|
+
);
|
|
9
|
+
const [rows] = await pool.query("SELECT * FROM users WHERE id = ?", [
|
|
10
|
+
result.insertId,
|
|
11
|
+
]);
|
|
12
|
+
return rows[0];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function getAllUsers() {
|
|
16
|
+
const [rows] = await pool.query("SELECT * FROM users ORDER BY id");
|
|
17
|
+
return rows;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function getUserById(id) {
|
|
21
|
+
const [rows] = await pool.query("SELECT * FROM users WHERE id = ?", [id]);
|
|
22
|
+
return rows[0] || null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function updateUser(id, updates) {
|
|
26
|
+
const { name, email } = updates;
|
|
27
|
+
await pool.query(
|
|
28
|
+
"UPDATE users SET name = ?, email = ? WHERE id = ?",
|
|
29
|
+
[name, email, id]
|
|
30
|
+
);
|
|
31
|
+
const [rows] = await pool.query("SELECT * FROM users WHERE id = ?", [id]);
|
|
32
|
+
return rows[0] || null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function deleteUser(id) {
|
|
36
|
+
const [result] = await pool.query("DELETE FROM users WHERE id = ?", [id]);
|
|
37
|
+
return result.affectedRows > 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import {
|
|
3
|
+
createUser,
|
|
4
|
+
getUsers,
|
|
5
|
+
getUserById,
|
|
6
|
+
updateUser,
|
|
7
|
+
deleteUser,
|
|
8
|
+
} from "../controllers/userController.js";
|
|
9
|
+
import { validateUserBody } from "../middleware/userMiddleware.js";
|
|
10
|
+
|
|
11
|
+
export const userRouter = express.Router();
|
|
12
|
+
|
|
13
|
+
// Create
|
|
14
|
+
userRouter.post("/", validateUserBody, createUser);
|
|
15
|
+
|
|
16
|
+
// Read all
|
|
17
|
+
userRouter.get("/", getUsers);
|
|
18
|
+
|
|
19
|
+
// Read one
|
|
20
|
+
userRouter.get("/:id", getUserById);
|
|
21
|
+
|
|
22
|
+
// Update
|
|
23
|
+
userRouter.put("/:id", validateUserBody, updateUser);
|
|
24
|
+
|
|
25
|
+
// Delete
|
|
26
|
+
userRouter.delete("/:id", deleteUser);
|
|
27
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "APP_NAME",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generated Node.js app (no database, in-memory user store)",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "node server.js",
|
|
8
|
+
"start": "node server.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"dotenv": "^16.4.0",
|
|
13
|
+
"express": "^4.19.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { createServer } from "./src/app.js";
|
|
3
|
+
|
|
4
|
+
const port = process.env.PORT || 4000;
|
|
5
|
+
|
|
6
|
+
async function start() {
|
|
7
|
+
const app = createServer();
|
|
8
|
+
|
|
9
|
+
app.listen(port, () => {
|
|
10
|
+
console.log(`Server running on http://localhost:${port}`);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
start().catch((err) => {
|
|
15
|
+
console.error("Failed to start server:", err);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
});
|
|
18
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { userRouter } from "./routes/userRoutes.js";
|
|
3
|
+
import { requestLogger } from "./middleware/logger.js";
|
|
4
|
+
import { notFound } from "./middleware/notFound.js";
|
|
5
|
+
import { errorHandler } from "./middleware/errorHandler.js";
|
|
6
|
+
|
|
7
|
+
export function createServer() {
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
app.use(express.json());
|
|
11
|
+
app.use(requestLogger);
|
|
12
|
+
|
|
13
|
+
app.get("/", (req, res) => {
|
|
14
|
+
res.json({ message: "Welcome to APP_NAME API (no database)" });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
app.use("/api/users", userRouter);
|
|
18
|
+
|
|
19
|
+
app.use(notFound);
|
|
20
|
+
app.use(errorHandler);
|
|
21
|
+
|
|
22
|
+
return app;
|
|
23
|
+
}
|
|
24
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createUser as createUserModel,
|
|
3
|
+
getAllUsers,
|
|
4
|
+
getUserById as getUserByIdModel,
|
|
5
|
+
updateUser as updateUserModel,
|
|
6
|
+
deleteUser as deleteUserModel,
|
|
7
|
+
} from "../models/userModel.js";
|
|
8
|
+
|
|
9
|
+
export async function createUser(req, res, next) {
|
|
10
|
+
try {
|
|
11
|
+
const user = createUserModel(req.body);
|
|
12
|
+
res.status(201).json(user);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
next(err);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getUsers(req, res, next) {
|
|
19
|
+
try {
|
|
20
|
+
res.json(getAllUsers());
|
|
21
|
+
} catch (err) {
|
|
22
|
+
next(err);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function getUserById(req, res, next) {
|
|
27
|
+
try {
|
|
28
|
+
const user = getUserByIdModel(req.params.id);
|
|
29
|
+
if (!user) {
|
|
30
|
+
return res.status(404).json({ message: "User not found" });
|
|
31
|
+
}
|
|
32
|
+
res.json(user);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
next(err);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function updateUser(req, res, next) {
|
|
39
|
+
try {
|
|
40
|
+
const user = updateUserModel(req.params.id, req.body);
|
|
41
|
+
if (!user) {
|
|
42
|
+
return res.status(404).json({ message: "User not found" });
|
|
43
|
+
}
|
|
44
|
+
res.json(user);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
next(err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function deleteUser(req, res, next) {
|
|
51
|
+
try {
|
|
52
|
+
const success = deleteUserModel(req.params.id);
|
|
53
|
+
if (!success) {
|
|
54
|
+
return res.status(404).json({ message: "User not found" });
|
|
55
|
+
}
|
|
56
|
+
res.status(204).send();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
next(err);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Generic error-handling middleware
|
|
2
|
+
export function errorHandler(err, req, res, next) {
|
|
3
|
+
console.error("Error:", err);
|
|
4
|
+
|
|
5
|
+
const status = err.status || 500;
|
|
6
|
+
const message = err.message || "Internal Server Error";
|
|
7
|
+
|
|
8
|
+
res.status(status).json({
|
|
9
|
+
message,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Basic request logger middleware
|
|
2
|
+
export function requestLogger(req, res, next) {
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
|
|
5
|
+
res.on("finish", () => {
|
|
6
|
+
const duration = Date.now() - start;
|
|
7
|
+
console.log(
|
|
8
|
+
`${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
next();
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Basic validation middleware for user payloads
|
|
2
|
+
export function validateUserBody(req, res, next) {
|
|
3
|
+
const { name, email } = req.body || {};
|
|
4
|
+
|
|
5
|
+
if (!name || !email) {
|
|
6
|
+
return res.status(400).json({
|
|
7
|
+
message: "Both 'name' and 'email' are required.",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
next();
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// In-memory user "model" for apps generated with --db none
|
|
2
|
+
|
|
3
|
+
let users = [];
|
|
4
|
+
let nextId = 1;
|
|
5
|
+
|
|
6
|
+
export function createUser(data) {
|
|
7
|
+
const user = { id: String(nextId++), ...data };
|
|
8
|
+
users.push(user);
|
|
9
|
+
return user;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getAllUsers() {
|
|
13
|
+
return users;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getUserById(id) {
|
|
17
|
+
return users.find((u) => u.id === String(id)) || null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function updateUser(id, updates) {
|
|
21
|
+
const idx = users.findIndex((u) => u.id === String(id));
|
|
22
|
+
if (idx === -1) return null;
|
|
23
|
+
|
|
24
|
+
users[idx] = { ...users[idx], ...updates };
|
|
25
|
+
return users[idx];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function deleteUser(id) {
|
|
29
|
+
const idx = users.findIndex((u) => u.id === String(id));
|
|
30
|
+
if (idx === -1) return false;
|
|
31
|
+
users.splice(idx, 1);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import {
|
|
3
|
+
createUser,
|
|
4
|
+
getUsers,
|
|
5
|
+
getUserById,
|
|
6
|
+
updateUser,
|
|
7
|
+
deleteUser,
|
|
8
|
+
} from "../controllers/userController.js";
|
|
9
|
+
import { validateUserBody } from "../middleware/userMiddleware.js";
|
|
10
|
+
|
|
11
|
+
export const userRouter = express.Router();
|
|
12
|
+
|
|
13
|
+
// Create
|
|
14
|
+
userRouter.post("/", validateUserBody, createUser);
|
|
15
|
+
|
|
16
|
+
// Read all
|
|
17
|
+
userRouter.get("/", getUsers);
|
|
18
|
+
|
|
19
|
+
// Read one
|
|
20
|
+
userRouter.get("/:id", getUserById);
|
|
21
|
+
|
|
22
|
+
// Update
|
|
23
|
+
userRouter.put("/:id", validateUserBody, updateUser);
|
|
24
|
+
|
|
25
|
+
// Delete
|
|
26
|
+
userRouter.delete("/:id", deleteUser);
|
|
27
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "APP_NAME",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generated Node.js app (PostgreSQL user CRUD)",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "node server.js",
|
|
8
|
+
"start": "node server.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"dotenv": "^16.4.0",
|
|
13
|
+
"express": "^4.19.0",
|
|
14
|
+
"pg": "^8.11.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { createServer } from "./src/app.js";
|
|
3
|
+
import { connectDB } from "./src/config/db.js";
|
|
4
|
+
|
|
5
|
+
const port = process.env.PORT || 4000;
|
|
6
|
+
|
|
7
|
+
async function start() {
|
|
8
|
+
await connectDB();
|
|
9
|
+
|
|
10
|
+
const app = createServer();
|
|
11
|
+
|
|
12
|
+
app.listen(port, () => {
|
|
13
|
+
console.log(`Server running on http://localhost:${port}`);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
start().catch((err) => {
|
|
18
|
+
console.error("Failed to start server:", err);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { userRouter } from "./routes/userRoutes.js";
|
|
3
|
+
import { requestLogger } from "./middleware/logger.js";
|
|
4
|
+
import { notFound } from "./middleware/notFound.js";
|
|
5
|
+
import { errorHandler } from "./middleware/errorHandler.js";
|
|
6
|
+
|
|
7
|
+
export function createServer() {
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
// Core middlewares
|
|
11
|
+
app.use(express.json());
|
|
12
|
+
app.use(requestLogger);
|
|
13
|
+
|
|
14
|
+
// Health check / root route
|
|
15
|
+
app.get("/", (req, res) => {
|
|
16
|
+
res.json({ message: "Welcome to APP_NAME API (PostgreSQL)" });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// User CRUD routes
|
|
20
|
+
app.use("/api/users", userRouter);
|
|
21
|
+
|
|
22
|
+
// Not found + error handlers
|
|
23
|
+
app.use(notFound);
|
|
24
|
+
app.use(errorHandler);
|
|
25
|
+
|
|
26
|
+
return app;
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pkg from "pg";
|
|
2
|
+
|
|
3
|
+
const { Pool } = pkg;
|
|
4
|
+
|
|
5
|
+
export const pool = new Pool({
|
|
6
|
+
host: process.env.PG_HOST || "localhost",
|
|
7
|
+
port: Number(process.env.PG_PORT) || 5432,
|
|
8
|
+
database: process.env.PG_DATABASE || "APP_NAME",
|
|
9
|
+
user: process.env.PG_USER || "postgres",
|
|
10
|
+
password: process.env.PG_PASSWORD || "password",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export async function connectDB() {
|
|
14
|
+
try {
|
|
15
|
+
await pool.query("SELECT 1");
|
|
16
|
+
await pool.query(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
18
|
+
id SERIAL PRIMARY KEY,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
email TEXT NOT NULL UNIQUE
|
|
21
|
+
);
|
|
22
|
+
`);
|
|
23
|
+
console.log("Connected to PostgreSQL");
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error("PostgreSQL connection error:", err.message);
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createUser as createUserModel,
|
|
3
|
+
getAllUsers,
|
|
4
|
+
getUserById as getUserByIdModel,
|
|
5
|
+
updateUser as updateUserModel,
|
|
6
|
+
deleteUser as deleteUserModel,
|
|
7
|
+
} from "../models/userModel.js";
|
|
8
|
+
|
|
9
|
+
export async function createUser(req, res, next) {
|
|
10
|
+
try {
|
|
11
|
+
const user = await createUserModel(req.body);
|
|
12
|
+
res.status(201).json(user);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
next(err);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getUsers(req, res, next) {
|
|
19
|
+
try {
|
|
20
|
+
const users = await getAllUsers();
|
|
21
|
+
res.json(users);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
next(err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function getUserById(req, res, next) {
|
|
28
|
+
try {
|
|
29
|
+
const user = await getUserByIdModel(req.params.id);
|
|
30
|
+
if (!user) {
|
|
31
|
+
return res.status(404).json({ message: "User not found" });
|
|
32
|
+
}
|
|
33
|
+
res.json(user);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
next(err);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function updateUser(req, res, next) {
|
|
40
|
+
try {
|
|
41
|
+
const user = await updateUserModel(req.params.id, req.body);
|
|
42
|
+
if (!user) {
|
|
43
|
+
return res.status(404).json({ message: "User not found" });
|
|
44
|
+
}
|
|
45
|
+
res.json(user);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
next(err);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function deleteUser(req, res, next) {
|
|
52
|
+
try {
|
|
53
|
+
const success = await deleteUserModel(req.params.id);
|
|
54
|
+
if (!success) {
|
|
55
|
+
return res.status(404).json({ message: "User not found" });
|
|
56
|
+
}
|
|
57
|
+
res.status(204).send();
|
|
58
|
+
} catch (err) {
|
|
59
|
+
next(err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Generic error-handling middleware
|
|
2
|
+
export function errorHandler(err, req, res, next) {
|
|
3
|
+
console.error("Error:", err);
|
|
4
|
+
|
|
5
|
+
const status = err.status || 500;
|
|
6
|
+
const message = err.message || "Internal Server Error";
|
|
7
|
+
|
|
8
|
+
res.status(status).json({
|
|
9
|
+
message,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Basic request logger middleware
|
|
2
|
+
export function requestLogger(req, res, next) {
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
|
|
5
|
+
res.on("finish", () => {
|
|
6
|
+
const duration = Date.now() - start;
|
|
7
|
+
console.log(
|
|
8
|
+
`${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`
|
|
9
|
+
);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
next();
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Basic validation middleware for user payloads
|
|
2
|
+
export function validateUserBody(req, res, next) {
|
|
3
|
+
const { name, email } = req.body || {};
|
|
4
|
+
|
|
5
|
+
if (!name || !email) {
|
|
6
|
+
return res.status(400).json({
|
|
7
|
+
message: "Both 'name' and 'email' are required.",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
next();
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { pool } from "../config/db.js";
|
|
2
|
+
|
|
3
|
+
export async function createUser(data) {
|
|
4
|
+
const { name, email } = data;
|
|
5
|
+
const result = await pool.query(
|
|
6
|
+
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
|
|
7
|
+
[name, email]
|
|
8
|
+
);
|
|
9
|
+
return result.rows[0];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function getAllUsers() {
|
|
13
|
+
const result = await pool.query("SELECT * FROM users ORDER BY id");
|
|
14
|
+
return result.rows;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function getUserById(id) {
|
|
18
|
+
const result = await pool.query("SELECT * FROM users WHERE id = $1", [id]);
|
|
19
|
+
return result.rows[0] || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function updateUser(id, updates) {
|
|
23
|
+
const { name, email } = updates;
|
|
24
|
+
const result = await pool.query(
|
|
25
|
+
"UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *",
|
|
26
|
+
[name, email, id]
|
|
27
|
+
);
|
|
28
|
+
return result.rows[0] || null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function deleteUser(id) {
|
|
32
|
+
const result = await pool.query("DELETE FROM users WHERE id = $1", [id]);
|
|
33
|
+
return result.rowCount > 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import {
|
|
3
|
+
createUser,
|
|
4
|
+
getUsers,
|
|
5
|
+
getUserById,
|
|
6
|
+
updateUser,
|
|
7
|
+
deleteUser,
|
|
8
|
+
} from "../controllers/userController.js";
|
|
9
|
+
import { validateUserBody } from "../middleware/userMiddleware.js";
|
|
10
|
+
|
|
11
|
+
export const userRouter = express.Router();
|
|
12
|
+
|
|
13
|
+
// Create
|
|
14
|
+
userRouter.post("/", validateUserBody, createUser);
|
|
15
|
+
|
|
16
|
+
// Read all
|
|
17
|
+
userRouter.get("/", getUsers);
|
|
18
|
+
|
|
19
|
+
// Read one
|
|
20
|
+
userRouter.get("/:id", getUserById);
|
|
21
|
+
|
|
22
|
+
// Update
|
|
23
|
+
userRouter.put("/:id", validateUserBody, updateUser);
|
|
24
|
+
|
|
25
|
+
// Delete
|
|
26
|
+
userRouter.delete("/:id", deleteUser);
|
|
27
|
+
|