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 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,3 @@
1
+ PORT=5001
2
+ NODE_ENV=development
3
+ MONGODB_URI=mongodb://127.0.0.1:27017/boilerplate
@@ -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,10 @@
1
+ const express = require('express');
2
+
3
+ const routes = require('./routes');
4
+
5
+ const app = express();
6
+
7
+ app.use(express.json());
8
+ app.use('/api', routes);
9
+
10
+ module.exports = app;
@@ -0,0 +1,12 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const connectDB = async () => {
4
+ const uri = process.env.MONGODB_URI;
5
+ if (!uri) {
6
+ throw new Error('MONGODB_URI is not set');
7
+ }
8
+
9
+ await mongoose.connect(uri);
10
+ };
11
+
12
+ module.exports = connectDB;
@@ -0,0 +1,7 @@
1
+ const healthCheck = (req, res) => {
2
+ res.status(200).json({ status: 'ok' });
3
+ };
4
+
5
+ module.exports = {
6
+ healthCheck,
7
+ };
@@ -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,9 @@
1
+ const express = require('express');
2
+
3
+ const { healthCheck } = require('../controllers/healthController');
4
+
5
+ const router = express.Router();
6
+
7
+ router.get('/', healthCheck);
8
+
9
+ module.exports = router;
@@ -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
+ });