create-express-mongo-mvc 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/README.md +48 -0
- package/bin/create-express-mongo-mvc.js +125 -0
- package/package.json +23 -0
- package/template/.env.example +3 -0
- package/template/README.md +20 -0
- package/template/package.json +19 -0
- package/template/src/app.js +10 -0
- package/template/src/config/db.js +12 -0
- package/template/src/controllers/healthController.js +7 -0
- package/template/src/controllers/userController.js +63 -0
- package/template/src/models/User.js +21 -0
- package/template/src/routes/healthRoutes.js +9 -0
- package/template/src/routes/index.js +11 -0
- package/template/src/routes/userRoutes.js +17 -0
- package/template/src/server.js +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Z# create-express-mongo-mvc
|
|
2
|
+
|
|
3
|
+
Create a minimal Express + MongoDB (Mongoose) MVC boilerplate.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-express-mongo-mvc my-app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or, scaffold and install dependencies in one go:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx create-express-mongo-mvc my-app --install
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What you get
|
|
18
|
+
|
|
19
|
+
- Express app (`src/app.js`)
|
|
20
|
+
- Server entry (`src/server.js`)
|
|
21
|
+
- Mongo connection (`src/config/db.js`)
|
|
22
|
+
- MVC folders (`controllers/`, `models/`, `routes/`)
|
|
23
|
+
- Example `User` CRUD + `health` route
|
|
24
|
+
|
|
25
|
+
## After scaffolding
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd my-app
|
|
29
|
+
cp .env.example .env
|
|
30
|
+
npm install
|
|
31
|
+
npm run dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Publishing
|
|
35
|
+
|
|
36
|
+
1. Login:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm login
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. Publish:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm publish
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If you want `npx create-...` behavior, the package name should start with `create-`.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const usage = () => {
|
|
8
|
+
console.log('Usage: create-express-mongo-mvc <project-name> [--install] [--skip-install]');
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const parseArgs = (argv) => {
|
|
12
|
+
const args = [];
|
|
13
|
+
const flags = new Set();
|
|
14
|
+
|
|
15
|
+
for (const arg of argv) {
|
|
16
|
+
if (arg.startsWith('-')) {
|
|
17
|
+
flags.add(arg);
|
|
18
|
+
} else {
|
|
19
|
+
args.push(arg);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
projectName: args[0],
|
|
25
|
+
install: flags.has('--install'),
|
|
26
|
+
skipInstall: flags.has('--skip-install'),
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const options = parseArgs(process.argv.slice(2));
|
|
31
|
+
const projectName = options.projectName;
|
|
32
|
+
|
|
33
|
+
if (!projectName) {
|
|
34
|
+
usage();
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const templateDir = path.join(__dirname, '..', 'template');
|
|
39
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
40
|
+
|
|
41
|
+
const pathExists = async (p) => {
|
|
42
|
+
try {
|
|
43
|
+
await fs.promises.access(p);
|
|
44
|
+
return true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const copyDir = async (srcDir, destDir) => {
|
|
51
|
+
await fs.promises.mkdir(destDir, { recursive: true });
|
|
52
|
+
const entries = await fs.promises.readdir(srcDir, { withFileTypes: true });
|
|
53
|
+
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.name === 'node_modules') continue;
|
|
56
|
+
|
|
57
|
+
const src = path.join(srcDir, entry.name);
|
|
58
|
+
const dest = path.join(destDir, entry.name);
|
|
59
|
+
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
await copyDir(src, dest);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (entry.isSymbolicLink()) {
|
|
66
|
+
const real = await fs.promises.readlink(src);
|
|
67
|
+
await fs.promises.symlink(real, dest);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await fs.promises.copyFile(src, dest);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const run = async () => {
|
|
76
|
+
if (await pathExists(targetDir)) {
|
|
77
|
+
console.error(`Target directory already exists: ${targetDir}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await copyDir(templateDir, targetDir);
|
|
82
|
+
|
|
83
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
84
|
+
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, 'utf8'));
|
|
85
|
+
pkg.name = projectName;
|
|
86
|
+
await fs.promises.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
87
|
+
|
|
88
|
+
const envExamplePath = path.join(targetDir, '.env.example');
|
|
89
|
+
const envPath = path.join(targetDir, '.env');
|
|
90
|
+
if ((await pathExists(envExamplePath)) && !(await pathExists(envPath))) {
|
|
91
|
+
await fs.promises.copyFile(envExamplePath, envPath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const shouldInstall = options.install && !options.skipInstall;
|
|
95
|
+
|
|
96
|
+
console.log('');
|
|
97
|
+
console.log(`Scaffolded project in: ${targetDir}`);
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('Next steps:');
|
|
100
|
+
console.log(` cd ${projectName}`);
|
|
101
|
+
|
|
102
|
+
if (shouldInstall) {
|
|
103
|
+
console.log(' npm install');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(' npm run dev');
|
|
107
|
+
console.log('');
|
|
108
|
+
|
|
109
|
+
if (shouldInstall) {
|
|
110
|
+
const res = spawnSync('npm', ['install'], {
|
|
111
|
+
cwd: targetDir,
|
|
112
|
+
stdio: 'inherit',
|
|
113
|
+
shell: process.platform === 'win32',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (res.status !== 0) {
|
|
117
|
+
process.exit(res.status || 1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
run().catch((err) => {
|
|
123
|
+
console.error(err);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-express-mongo-mvc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a minimal Express + MongoDB (Mongoose) MVC boilerplate",
|
|
5
|
+
"main": "bin/create-express-mongo-mvc.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node bin/create-express-mongo-mvc.js",
|
|
8
|
+
"lint": "node -c bin/create-express-mongo-mvc.js",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"create-express-mongo-mvc": "bin/create-express-mongo-mvc.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/",
|
|
16
|
+
"template/"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"type": "commonjs"
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# __APP_NAME__
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
1. Create `.env` from `.env.example`
|
|
6
|
+
2. Install dependencies
|
|
7
|
+
3. Run the server
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Endpoints
|
|
15
|
+
|
|
16
|
+
- `GET /api/health`
|
|
17
|
+
- `GET /api/users`
|
|
18
|
+
- `POST /api/users`
|
|
19
|
+
- `PUT /api/users/:id`
|
|
20
|
+
- `DELETE /api/users/:id`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "express-mongo-mvc-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "src/server.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node src/server.js",
|
|
9
|
+
"dev": "nodemon src/server.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"dotenv": "^16.4.5",
|
|
13
|
+
"express": "^4.19.2",
|
|
14
|
+
"mongoose": "^8.9.5"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"nodemon": "^3.1.9"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const User = require('../models/User');
|
|
2
|
+
|
|
3
|
+
const getUsers = async (req, res, next) => {
|
|
4
|
+
try {
|
|
5
|
+
const users = await User.find().sort({ createdAt: -1 });
|
|
6
|
+
res.status(200).json({ data: users });
|
|
7
|
+
} catch (err) {
|
|
8
|
+
next(err);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const createUser = async (req, res, next) => {
|
|
13
|
+
try {
|
|
14
|
+
const { name, email } = req.body;
|
|
15
|
+
const user = await User.create({ name, email });
|
|
16
|
+
res.status(201).json({ data: user });
|
|
17
|
+
} catch (err) {
|
|
18
|
+
next(err);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const updateUser = async (req, res, next) => {
|
|
23
|
+
try {
|
|
24
|
+
const { id } = req.params;
|
|
25
|
+
const { name, email } = req.body;
|
|
26
|
+
|
|
27
|
+
const user = await User.findByIdAndUpdate(
|
|
28
|
+
id,
|
|
29
|
+
{ name, email },
|
|
30
|
+
{ new: true, runValidators: true }
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
if (!user) {
|
|
34
|
+
return res.status(404).json({ message: 'User not found' });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
res.status(200).json({ data: user });
|
|
38
|
+
} catch (err) {
|
|
39
|
+
next(err);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const deleteUser = async (req, res, next) => {
|
|
44
|
+
try {
|
|
45
|
+
const { id } = req.params;
|
|
46
|
+
const user = await User.findByIdAndDelete(id);
|
|
47
|
+
|
|
48
|
+
if (!user) {
|
|
49
|
+
return res.status(404).json({ message: 'User not found' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
res.status(200).json({ data: user });
|
|
53
|
+
} catch (err) {
|
|
54
|
+
next(err);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
getUsers,
|
|
60
|
+
createUser,
|
|
61
|
+
updateUser,
|
|
62
|
+
deleteUser,
|
|
63
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const mongoose = require('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
|
+
unique: true,
|
|
14
|
+
lowercase: true,
|
|
15
|
+
trim: true,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{ timestamps: true }
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
module.exports = mongoose.model('User', userSchema);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
|
|
3
|
+
const healthRoutes = require('./healthRoutes');
|
|
4
|
+
const userRoutes = require('./userRoutes');
|
|
5
|
+
|
|
6
|
+
const router = express.Router();
|
|
7
|
+
|
|
8
|
+
router.use('/health', healthRoutes);
|
|
9
|
+
router.use('/users', userRoutes);
|
|
10
|
+
|
|
11
|
+
module.exports = router;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getUsers,
|
|
5
|
+
createUser,
|
|
6
|
+
updateUser,
|
|
7
|
+
deleteUser,
|
|
8
|
+
} = require('../controllers/userController');
|
|
9
|
+
|
|
10
|
+
const router = express.Router();
|
|
11
|
+
|
|
12
|
+
router.get('/', getUsers);
|
|
13
|
+
router.post('/', createUser);
|
|
14
|
+
router.put('/:id', updateUser);
|
|
15
|
+
router.delete('/:id', deleteUser);
|
|
16
|
+
|
|
17
|
+
module.exports = router;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
|
|
3
|
+
const app = require('./app');
|
|
4
|
+
const connectDB = require('./config/db');
|
|
5
|
+
|
|
6
|
+
const PORT = process.env.PORT || 5000;
|
|
7
|
+
|
|
8
|
+
const start = async () => {
|
|
9
|
+
await connectDB();
|
|
10
|
+
|
|
11
|
+
app.listen(PORT, () => {
|
|
12
|
+
console.log(`Server listening on port ${PORT}`);
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
start().catch((err) => {
|
|
17
|
+
console.error(err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|