create-express-modular 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 levi9111
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ ```markdown
2
+ # πŸš€ Create Express Modular
3
+
4
+ > A powerful interactive CLI to scaffold a scalable, database‑agnostic Express + TypeScript server β€” inspired by NestJS structure, but lightweight and flexible.
5
+
6
+ [![npm version](https://img.shields.io/npm/v/create-express-modular.svg)](https://www.npmjs.com/package/create-express-modular)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ Stop copying boilerplate manually. Define your modules once, and let `create-express-modular` generate a production‑ready, strictly typed Express application in seconds.
10
+
11
+ ## ✨ Why use this?
12
+
13
+ - **Interactive Scaffolding** – The CLI asks for your module names (e.g. `User, Product, Order`) and builds the entire folder structure and initial files automatically.
14
+ - **Database Agnostic** – No ORM is forced. Works equally well with MongoDB (Mongoose), PostgreSQL (Prisma/TypeORM), MySQL, or plain SQL.
15
+ - **NestJS‑inspired Modular Design** – Each module contains its own controller, service, routes, model, interface, and validation β€” separation of concerns out of the box.
16
+ - **Auto‑wired Routing** – Your custom modules are automatically imported and registered in `src/app/routes/index.ts`. No manual wiring required.
17
+ - **Production‑ready DX** – TypeScript strict mode, ESLint (v8), Prettier, and `ts-node-dev` for instant hot‑reload during development.
18
+
19
+ ## πŸ“¦ Quick Start
20
+
21
+ You don't need to install anything globally. Use `npx` to run the latest version:
22
+
23
+ ```bash
24
+ npx create-express-modular
25
+ ```
26
+
27
+ The CLI will guide you through two simple steps:
28
+ 1. **Project name** – Choose a name for your new backend.
29
+ 2. **Module names** – List the feature modules you need (e.g. `Auth, Receipt, Property`).
30
+
31
+ Once finished, your project is ready with all dependencies installed and Git initialised.
32
+
33
+ ## πŸ“‚ Generated Project Structure
34
+
35
+ After generating a project with a `User` module, your `src` directory will look like this:
36
+
37
+ ```
38
+ src/
39
+ β”œβ”€β”€ app/
40
+ β”‚ β”œβ”€β”€ builder/
41
+ β”‚ β”œβ”€β”€ config/
42
+ β”‚ β”œβ”€β”€ constants/
43
+ β”‚ β”œβ”€β”€ errors/
44
+ β”‚ β”œβ”€β”€ interface/
45
+ β”‚ β”œβ”€β”€ middlewares/
46
+ β”‚ β”œβ”€β”€ modules/
47
+ β”‚ β”‚ └── User/ # ✨ Generated module
48
+ β”‚ β”‚ β”œβ”€β”€ user.controller.ts
49
+ β”‚ β”‚ β”œβ”€β”€ user.interface.ts
50
+ β”‚ β”‚ β”œβ”€β”€ user.model.ts
51
+ β”‚ β”‚ β”œβ”€β”€ user.route.ts
52
+ β”‚ β”‚ β”œβ”€β”€ user.service.ts
53
+ β”‚ β”‚ β”œβ”€β”€ user.utils.ts
54
+ β”‚ β”‚ └── user.validation.ts
55
+ β”‚ β”œβ”€β”€ routes/
56
+ β”‚ β”‚ └── index.ts # ✨ Auto‑wired with your modules
57
+ β”‚ └── utils/
58
+ β”œβ”€β”€ app.ts
59
+ └── server.ts
60
+ ```
61
+
62
+ > **Tip:** The architecture is domain‑driven and easily extensible β€” perfect for growing APIs.
63
+
64
+ ## πŸ“œ Built‑in Scripts
65
+
66
+ Inside your generated project, you can run:
67
+
68
+ | Command | Description |
69
+ |-----------------------|-----------------------------------------------------------|
70
+ | `npm run start:dev` | Starts the dev server with hot‑reload (`ts-node-dev`) |
71
+ | `npm run build` | Compiles TypeScript to JavaScript (`dist/`) |
72
+ | `npm start` | Runs the compiled app in production |
73
+ | `npm run lint` | Lints the codebase with ESLint |
74
+ | `npm run lint:fix` | Automatically fixes linting issues |
75
+ | `npm run prettier` | Formats all files using Prettier |
76
+
77
+ ## 🧩 Customisation & Extensibility
78
+
79
+ Because the tool generates standard Express + TypeScript code, you can easily add any middleware, ORM, or utility library. The structure is designed to stay out of your way while keeping everything organised.
80
+
81
+ ## 🀝 Contributing
82
+
83
+ We welcome contributions! Feel free to open an issue or submit a pull request on [GitHub](https://github.com/Levi9111/npm-create-express-modular).
84
+
85
+ ## πŸ“„ License
86
+
87
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
88
+
package/bin/cli.js ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const {
6
+ execSync
7
+ } = require('child_process');
8
+ const readline = require('readline');
9
+
10
+ const rl = readline.createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout,
13
+ });
14
+
15
+ const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
16
+
17
+ async function runCLI() {
18
+ console.log('\nπŸš€ Welcome to Create Express Modular!\n');
19
+
20
+ // 1. Get Project Name
21
+ let projectName = process.argv[2];
22
+ if (!projectName) {
23
+ projectName = await askQuestion('πŸ“¦ What is your project named? (e.g., my-api): ');
24
+ }
25
+
26
+ if (!projectName) {
27
+ console.error('❌ Project name is required.');
28
+ process.exit(1);
29
+ }
30
+
31
+ // 2. Ask for Modules
32
+ const modulesInput = await askQuestion(
33
+ 'πŸ› οΈ Which modules should we generate? (comma-separated, e.g., User,Product,Order): '
34
+ );
35
+
36
+ rl.close();
37
+
38
+ const currentPath = process.cwd();
39
+ const projectPath = path.join(currentPath, projectName);
40
+ const templatePath = path.join(__dirname, '../template');
41
+
42
+ // 3. Create Project Directory
43
+ try {
44
+ fs.mkdirSync(projectPath);
45
+ } catch (err) {
46
+ if (err.code === 'EEXIST') {
47
+ console.error(`❌ Directory '${projectName}' already exists.`);
48
+ } else {
49
+ console.error(err);
50
+ }
51
+ process.exit(1);
52
+ }
53
+
54
+ // Helper to copy the base template
55
+ function copyFolderSync(from, to) {
56
+ fs.mkdirSync(to, {
57
+ recursive: true
58
+ });
59
+ fs.readdirSync(from).forEach((element) => {
60
+ const fromPath = path.join(from, element);
61
+ const toPath = path.join(to, element);
62
+ if (fs.lstatSync(fromPath).isFile()) {
63
+ fs.copyFileSync(fromPath, toPath);
64
+ } else {
65
+ copyFolderSync(fromPath, toPath);
66
+ }
67
+ });
68
+ }
69
+
70
+ console.log(`\nπŸ“‚ Scaffolding base architecture...`);
71
+ copyFolderSync(templatePath, projectPath);
72
+
73
+ // 4. Rename gitignore to .gitignore
74
+ const gitignorePath = path.join(projectPath, 'gitignore');
75
+ if (fs.existsSync(gitignorePath)) {
76
+ fs.renameSync(gitignorePath, path.join(projectPath, '.gitignore'));
77
+ }
78
+
79
+ // 5. Update the new package.json with the user's project name
80
+ const packageJsonPath = path.join(projectPath, 'package.json');
81
+ if (fs.existsSync(packageJsonPath)) {
82
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
83
+ packageJson.name = projectName.toLowerCase().replace(/\s+/g, '-');
84
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
85
+ }
86
+
87
+ // 6. Dynamically Generate the Modules based on user input
88
+ const modulesToCreate = modulesInput
89
+ .split(',')
90
+ .map((m) => m.trim())
91
+ .filter((m) => m.length > 0);
92
+
93
+ if (modulesToCreate.length > 0) {
94
+ console.log(`\n🧩 Generating custom modules...`);
95
+
96
+ const modulesBaseDir = path.join(projectPath, 'src/app/modules');
97
+ if (!fs.existsSync(modulesBaseDir)) fs.mkdirSync(modulesBaseDir, {
98
+ recursive: true
99
+ });
100
+
101
+ let routeImports = '';
102
+ let routeArray = 'const moduleRoutes = [\n';
103
+
104
+ modulesToCreate.forEach((mod) => {
105
+ const moduleName = mod.charAt(0).toUpperCase() + mod.slice(1);
106
+ const fileName = mod.toLowerCase();
107
+ const modulePath = path.join(modulesBaseDir, moduleName);
108
+
109
+ fs.mkdirSync(modulePath, {
110
+ recursive: true
111
+ });
112
+
113
+ // Create the 7 standard files for each module
114
+ const fileTypes = ['controller', 'interface', 'model', 'route', 'service', 'utils', 'validation'];
115
+ fileTypes.forEach((type) => {
116
+ fs.writeFileSync(
117
+ path.join(modulePath, `${fileName}.${type}.ts`),
118
+ `// TODO: Implement ${moduleName} ${type}\n`
119
+ );
120
+ });
121
+
122
+ console.log(` - Created ${moduleName} module`);
123
+
124
+ // Prepare data for the routes/index.ts file
125
+ routeImports += `import { ${moduleName}Routes } from '../modules/${moduleName}/${fileName}.route';\n`;
126
+ routeArray += ` { path: '/${fileName}s', route: ${moduleName}Routes },\n`;
127
+ });
128
+
129
+ routeArray += '];\n';
130
+
131
+ // 7. Generate src/app/routes/index.ts to wire up the router automatically
132
+ const routesDir = path.join(projectPath, 'src/app/routes');
133
+ if (!fs.existsSync(routesDir)) fs.mkdirSync(routesDir, {
134
+ recursive: true
135
+ });
136
+
137
+ const routeFileContent = `import { Router } from 'express';\n\n${routeImports}\nconst router = Router();\n\n${routeArray}\nmoduleRoutes.forEach((route) => router.use(route.path, route.route));\n\nexport default router;\n`;
138
+ fs.writeFileSync(path.join(routesDir, 'index.ts'), routeFileContent);
139
+ }
140
+
141
+ // 8. Install Dependencies
142
+ console.log('\nπŸ“¦ Installing dependencies (this takes a minute)...');
143
+ try {
144
+ execSync('npm install', {
145
+ cwd: projectPath,
146
+ stdio: 'inherit'
147
+ });
148
+ } catch (error) {
149
+ console.error('\n❌ Failed to install dependencies.');
150
+ }
151
+
152
+ console.log(`\nβœ… Success! Your Express architecture is ready.`);
153
+ console.log(`\nNext steps:`);
154
+ console.log(` cd ${projectName}`);
155
+ console.log(` npm run start:dev\n`);
156
+ }
157
+
158
+ runCLI();
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "create-express-modular",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a modular Express TS server",
5
+ "bin": {
6
+ "create-express-modular": "./bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "template",
11
+ "README.md",
12
+ "package.json",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "keywords": [
19
+ "express",
20
+ "typescript",
21
+ "modular",
22
+ "scaffold",
23
+ "cli"
24
+ ],
25
+ "author": "levi9111",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/Levi9111/npm-create-express-modular.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/Levi9111/npm-create-express-modular/issues"
33
+ },
34
+ "homepage": "https://github.com/Levi9111/npm-create-express-modular#readme",
35
+ "dependencies": {
36
+ "http-status-codes": "^2.3.0"
37
+ }
38
+ }
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ .env
@@ -0,0 +1,27 @@
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es2021": true
5
+ },
6
+ "extends": [
7
+ "eslint:recommended",
8
+ "plugin:@typescript-eslint/recommended",
9
+ "prettier"
10
+ ],
11
+ "parser": "@typescript-eslint/parser",
12
+ "parserOptions": {
13
+ "ecmaVersion": "latest",
14
+ "sourceType": "module"
15
+ },
16
+ "plugins": ["@typescript-eslint"],
17
+ "rules": {
18
+ "no-unused-vars": "error",
19
+ "no-unused-expressions": "error",
20
+ "prefer-const": "error",
21
+ "no-console": "warn",
22
+ "no-undef": "error"
23
+ },
24
+ "globals": {
25
+ "process": "readonly"
26
+ }
27
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "tabWidth": 2,
5
+ "trailingComma": "all",
6
+ "printWidth": 80,
7
+ "jsxSingleQuote": true,
8
+ "jsxBracketSameLine": false,
9
+ "arrowParens": "always",
10
+ "bracketSpacing": true,
11
+ "endOfLine": "auto"
12
+ }
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .env.local
5
+ *.log
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "template",
3
+ "version": "1.0.0",
4
+ "main": "src/server.ts",
5
+ "scripts": {
6
+ "build": "npm install && tsc",
7
+ "start": "node dist/server.js",
8
+ "start:dev": "ts-node-dev --respawn --transpile-only src/server.ts",
9
+ "lint": "eslint src --ignore-path .eslintignore --ext .ts",
10
+ "lint:fix": "eslint src --fix --ignore-path .eslintignore --ext .ts",
11
+ "prettier": "prettier --ignore-path .gitignore --write \"./src/**/*.{js,ts,json}\"",
12
+ "prettier:fix": "prettier --write src"
13
+ },
14
+ "dependencies": {
15
+ "cors": "^2.8.5",
16
+ "dotenv": "^16.3.1",
17
+ "express": "^4.18.2"
18
+ },
19
+ "devDependencies": {
20
+ "@types/cors": "^2.8.13",
21
+ "@types/express": "^4.17.17",
22
+ "@types/node": "^20.4.5",
23
+ "@typescript-eslint/eslint-plugin": "^6.2.0",
24
+ "@typescript-eslint/parser": "^6.2.0",
25
+ "eslint": "^8.45.0",
26
+ "eslint-config-prettier": "^8.8.0",
27
+ "prettier": "^3.0.0",
28
+ "ts-node-dev": "^2.0.0",
29
+ "typescript": "^5.1.6"
30
+ }
31
+ }
@@ -0,0 +1,12 @@
1
+ class AppError extends Error {
2
+ public statusCode: number;
3
+
4
+ constructor(statusCode: number, message: string, stack: string = '') {
5
+ super(message);
6
+ this.statusCode = statusCode;
7
+ if (stack) this.stack = stack;
8
+ else Error.captureStackTrace(this, this.constructor);
9
+ }
10
+ }
11
+
12
+ export default AppError;
@@ -0,0 +1,26 @@
1
+ import { ErrorRequestHandler } from 'express';
2
+ import AppError from '../errors/AppError';
3
+
4
+ const globalErrorHandler: ErrorRequestHandler = (err, req, res, next) => {
5
+ let statusCode = 500;
6
+ let message = 'Something went wrong!';
7
+ let errorMessages: { path: string | number; message: string }[] = [];
8
+
9
+ if (err instanceof AppError) {
10
+ statusCode = err.statusCode;
11
+ message = err.message;
12
+ errorMessages = err.message ? [{ path: '', message: err.message }] : [];
13
+ } else if (err instanceof Error) {
14
+ message = err.message;
15
+ errorMessages = err.message ? [{ path: '', message: err.message }] : [];
16
+ }
17
+
18
+ res.status(statusCode).json({
19
+ success: false,
20
+ message,
21
+ errorMessages,
22
+ stack: process.env.NODE_ENV !== 'production' ? err.stack : undefined,
23
+ });
24
+ };
25
+
26
+ export default globalErrorHandler;
@@ -0,0 +1,15 @@
1
+ /* eslint-disable no-unused-vars */
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+
4
+ import { NextFunction, Request, Response } from 'express';
5
+ import httpStatus from 'http-status-codes';
6
+
7
+ const notFound = (req: Request, res: Response, next: NextFunction) => {
8
+ return res.status(httpStatus.NOT_FOUND).json({
9
+ success: false,
10
+ message: 'API Not Found !!',
11
+ error: '',
12
+ });
13
+ };
14
+
15
+ export default notFound;
@@ -0,0 +1,5 @@
1
+ import { Router } from 'express';
2
+
3
+ const router = Router();
4
+
5
+ export default router;
@@ -0,0 +1,23 @@
1
+ import cors from 'cors';
2
+ import express, { Application, Request, Response } from 'express';
3
+ import router from './app/routes/index';
4
+ import notFound from './app/middlewares/notFound';
5
+ import globalErrorHandler from './app/middlewares/globalErrorhandler';
6
+
7
+ const app: Application = express();
8
+
9
+ //parsers
10
+ app.use(express.json());
11
+ app.use(cors());
12
+
13
+ // application routes
14
+ app.use('/api/v1', router);
15
+
16
+ app.get('/', (_req: Request, res: Response) => {
17
+ res.send('Welcome to Express Modular Server!');
18
+ });
19
+
20
+ app.use(globalErrorHandler);
21
+ app.use(notFound);
22
+
23
+ export default app;
@@ -0,0 +1,15 @@
1
+ import app from './app';
2
+
3
+ const port = 5000;
4
+
5
+ async function bootstrap() {
6
+ try {
7
+ app.listen(port, () => {
8
+ console.log(`Server is running on port ${port}`);
9
+ });
10
+ } catch (err) {
11
+ console.log(err);
12
+ }
13
+ }
14
+
15
+ bootstrap();
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "rootDir": "./src",
7
+ "outDir": "./dist",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "baseUrl": ".",
13
+ "paths": {
14
+ "@/*": ["src/*"]
15
+ }
16
+ },
17
+ "include": ["src/**/*.ts"],
18
+ "exclude": ["node_modules"]
19
+ }