create-express-kickstart 1.2.4 → 1.2.6

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.
Files changed (3) hide show
  1. package/README.md +55 -9
  2. package/bin/cli.js +49 -29
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,9 +6,36 @@
6
6
 
7
7
  A powerful CLI tool to instantly scaffold a production-ready, feature-rich backend Node.js template specifically tailored for Express API applications. It adheres to modern best practices, providing standard structures for error handling, CORS setups, routing, and middlewares right out of the box.
8
8
 
9
+ ## What is `create-express-kickstart`?
10
+
11
+ **The Purpose:**
12
+ Whenever developers start a new Node.js & Express.js project, they often spend the first couple of hours writing the exact same boilerplate: configuring `express`, setting up `cors`, managing `dotenv`, writing global error handlers, standardizing API responses, and configuring database connections. `create-express-kickstart` exists to entirely eliminate this repetitive chore. Our goal is to provide a production-grade backend right out of the gate so you can immediately focus on writing business logic. AI assistants and developers alike can rely on this tool to reliably scaffold consistent, secure, and modern Node.js backends.
13
+
14
+ **What It Does:**
15
+ It is an interactive CLI framework scaffolding generator. Upon running the command, it asks you a series of simple questions regarding the architecture of your new API (e.g., Do you want MongoDB? Do you want JWT Auth Boilerplate? Docker? Jest for testing?). Based on your exact answers, it instantly generates a fully configured, running codebase tailored exclusively to your project's needs.
16
+
17
+ **How It Works:**
18
+ Under the hood, the CLI runs dynamically directly from NPM via `npx` executing a Node.js compiler script:
19
+ 1. **Interactive Prompting:** The CLI polls for your configurations in real-time.
20
+ 2. **Selective Templating:** It recursively copies a pre-configured, highly modular `src` application design into your directory.
21
+ 3. **Intelligent Modding:** If you selectively opt-out of specific modules (like CORS, Pino, or Mongoose), the CLI acts like an auto-linter—parsing and cleanly stripping those specific unused imports and middleware usages cleanly out of the core application. You only receive the code you explicitly asked for, preventing dead-weight execution bloat.
22
+ 4. **Smart Dependency Resolution:** Ultimately, it dynamically computes a final `package.json` structure wrapping all selected tools directly toward their `latest` versions, seamlessly bypassing localized cache bugs—subsequently initiating the true install layer utilizing your selected manager (`npm`, `yarn`, `pnpm`, or `bun`).
23
+
24
+ **What is Inside (The Architecture):**
25
+ The generated Express template champions the **MVC (Model-View-Controller)** pattern with robust modern Node.js Path Aliasing bindings enabled out of the box:
26
+ - **/src/controllers** - Functional logic handlers.
27
+ - **/src/routes** - Isolated Express routers mapping precise endpoints to controllers.
28
+ - **/src/middlewares** - Pre-configured intercepts including a robust Global errorHandler.
29
+ - **/src/utils** - Core toolkit items mapped globally across the codebase. Highlights include:
30
+ - `ApiResponse` structure class for predictable and formatted JSON HTTP payloads.
31
+ - `ApiError` extension class for standardizing HTTP error interceptions.
32
+ - `asyncHandler` functional wrapper intercepting promise rejections seamlessly to avoid repetitive try-catch blocks in your controllers!
33
+ - **Optional Add-ons** - Complete JWT Authentication logic integration featuring secure cryptographic generation functions (`bcryptjs`), standardized .env setups, Dockerfile templates, and Jest assertion pipelines.
34
+
35
+
9
36
  ---
10
37
 
11
- ## 🚀 Getting Started
38
+ ## Getting Started
12
39
 
13
40
  You do not need to clone this repository, install dependencies manually, or write an initial configuration yourself. Use `npx` (which comes with npm 5.2+) to instantly generate your backend boilerplate!
14
41
 
@@ -41,7 +68,7 @@ npm run dev
41
68
 
42
69
  ---
43
70
 
44
- ## 🌟 Features
71
+ ## Features
45
72
 
46
73
  - **Modern JavaScript**: ES6 Modules (`import`/`export`) enabled by default.
47
74
  - **Robust Error Handling**: Centralized error management using custom `ApiError` and `errorHandler` middleware.
@@ -54,7 +81,7 @@ npm run dev
54
81
 
55
82
  ---
56
83
 
57
- ## 🛠️ Core Utilities Built-In
84
+ ## Core Utilities Built-In
58
85
 
59
86
  This template shines in its standardized utilities available out of the box for you:
60
87
 
@@ -82,10 +109,29 @@ const restrictedRoute = asyncHandler(async (req, res) => {
82
109
 
83
110
  ### `asyncHandler`
84
111
  A wrapper for your async route handlers that eliminates the need for repetitive `try-catch` blocks.
85
- ## 🤝 Contributing & Repository
86
-
87
- Love this tool? Want to add a feature or fix a bug?
88
- Feel free to open an issue or submit a pull request on our GitHub Repository!
89
-
90
- 🔗 **GitHub Repository:** [https://github.com/aasifashraf/create-express-kickstart](https://github.com/aasifashraf/create-express-kickstart)
112
+
113
+ ### `hash.util.js`
114
+ If you choose to install the basic JWT Auth boilerplate, we automatically generate a generic hashing utility utilizing `bcryptjs` to help you securely hash and compare data (like passwords) natively.
115
+ ```javascript
116
+ import { hashData, compareData } from "#utils/hash.util.js";
117
+
118
+ const registerUser = asyncHandler(async (req, res) => {
119
+ const hashedPassword = await hashData("supersecret123");
120
+ // Store hashedPassword...
121
+ });
122
+
123
+ const loginUser = asyncHandler(async (req, res) => {
124
+ const isMatch = await compareData("supersecret123", user.hashedPassword);
125
+ // ...
126
+ });
127
+ ```
128
+
129
+ ## Contributing & Repository
130
+
131
+ Love this tool? Want to add a feature or fix a bug?
132
+ Feel free to open an issue or submit a pull request on our GitHub Repository!
133
+
134
+ **GitHub Repository:** [https://github.com/aasifashraf/create-express-kickstart](https://github.com/aasifashraf/create-express-kickstart)
135
+
136
+ **NPM Package:** [https://www.npmjs.com/package/create-express-kickstart](https://www.npmjs.com/package/create-express-kickstart)
91
137
 
package/bin/cli.js CHANGED
@@ -21,11 +21,11 @@ async function init() {
21
21
 
22
22
  let projectName = projectNameArg;
23
23
  if (!projectName) {
24
- projectName = await question('\n👉 Project Directory Name (e.g. my-awesome-api): ');
24
+ projectName = await question('\n> Project Directory Name (e.g. my-awesome-api): ');
25
25
  }
26
26
 
27
27
  if (!projectName) {
28
- console.error('\n❌ Error: Project Directory Name is required.');
28
+ console.error('\nError: Project Directory Name is required.');
29
29
  process.exit(1);
30
30
  }
31
31
 
@@ -33,19 +33,19 @@ async function init() {
33
33
  const projectPath = path.join(currentPath, projectName);
34
34
 
35
35
  if (fs.existsSync(projectPath)) {
36
- console.error(`\n❌ Error: Folder ${projectName} already exists. Please choose a different directory name.\n`);
36
+ console.error(`\nError: Folder ${projectName} already exists. Please choose a different directory name.\n`);
37
37
  process.exit(1);
38
38
  }
39
39
 
40
- let packageJsonName = await question(`👉 package.json name (${projectName}): `);
40
+ let packageJsonName = await question(`> package.json name (${projectName}): `);
41
41
  if (!packageJsonName.trim()) {
42
42
  packageJsonName = projectName; // Fallback to directory name
43
43
  }
44
44
 
45
- const description = await question('👉 Project description: ');
46
- const author = await question('👉 Author name: ');
45
+ const description = await question('> Project description: ');
46
+ const author = await question('> Author name: ');
47
47
 
48
- console.log('\n--- 📦 Select Dependencies ---');
48
+ console.log('\n--- Select Dependencies ---');
49
49
  console.log('Press Enter for Yes (Y), type "n" for No.\n');
50
50
 
51
51
  const deps = {
@@ -65,19 +65,19 @@ async function init() {
65
65
  installPinoPretty = (await question('Include pino-pretty for clean development logs? [Y/n] ')).toLowerCase() !== 'n';
66
66
  }
67
67
 
68
- const packageManagerChoice = await question('\n👉 Which package manager would you like to use? [npm/yarn/pnpm/bun] (default: npm): ');
68
+ const packageManagerChoice = await question('\n> Which package manager would you like to use? [npm/yarn/pnpm/bun] (default: npm): ');
69
69
  const packageManager = ['yarn', 'pnpm', 'bun'].includes(packageManagerChoice.trim().toLowerCase())
70
70
  ? packageManagerChoice.trim().toLowerCase()
71
71
  : 'npm';
72
72
 
73
- const initGit = (await question('\n👉 Initialize a git repository? [Y/n] ')).toLowerCase() !== 'n';
74
- const initDocker = (await question('👉 Include Dockerfile & docker-compose.yml? [Y/n] ')).toLowerCase() !== 'n';
75
- const initAuth = (await question('👉 Include basic JWT Auth boilerplate? [Y/n] ')).toLowerCase() !== 'n';
76
- const initTests = (await question('👉 Include Jest setup and boilerplate tests? [Y/n] ')).toLowerCase() !== 'n';
73
+ const initGit = (await question('\n> Initialize a git repository? [Y/n] ')).toLowerCase() !== 'n';
74
+ const initDocker = (await question('> Include Dockerfile & docker-compose.yml? [Y/n] ')).toLowerCase() !== 'n';
75
+ const initAuth = (await question('> Include basic JWT Auth boilerplate? [Y/n] ')).toLowerCase() !== 'n';
76
+ const initTests = (await question('> Include Jest setup and boilerplate tests? [Y/n] ')).toLowerCase() !== 'n';
77
77
 
78
78
  rl.close();
79
79
 
80
- console.log(`\n🚀 Creating a new Node.js Express API in ${projectPath}...`);
80
+ console.log(`\n Creating a new Node.js Express API in ${projectPath}...`);
81
81
  fs.mkdirSync(projectPath, { recursive: true });
82
82
 
83
83
  function copyRecursiveSync(src, dest) {
@@ -99,15 +99,15 @@ async function init() {
99
99
  const targetSrcDir = path.join(projectPath, 'src');
100
100
 
101
101
  if (!fs.existsSync(sourceDir)) {
102
- console.error('\n❌ Error: Could not find "src" directory in the template generator.');
102
+ console.error('\nError: Could not find "src" directory in the template generator.');
103
103
  process.exit(1);
104
104
  }
105
105
 
106
- console.log(`📂 Bootstrapping application structure (errorHandler, ApiResponse, async handlers)...`);
106
+ console.log(` Bootstrapping application structure (errorHandler, ApiResponse, async handlers)...`);
107
107
  copyRecursiveSync(sourceDir, targetSrcDir);
108
108
 
109
109
  // 2. Copy .env.example
110
- console.log(`🔧 Generating environment files...`);
110
+ console.log(` Generating environment files...`);
111
111
  const envExamplePath = path.join(__dirname, '..', '.env.example');
112
112
  if (fs.existsSync(envExamplePath)) {
113
113
  fs.copyFileSync(envExamplePath, path.join(projectPath, '.env.example'));
@@ -115,7 +115,7 @@ async function init() {
115
115
  }
116
116
 
117
117
  if (initDocker) {
118
- console.log(`🐳 Adding Docker files...`);
118
+ console.log(` Adding Docker files...`);
119
119
  const dockerfilePath = path.join(__dirname, '..', 'templates', 'Dockerfile');
120
120
  const dockerComposePath = path.join(__dirname, '..', 'templates', 'docker-compose.yml');
121
121
 
@@ -127,7 +127,7 @@ async function init() {
127
127
  }
128
128
 
129
129
  if (initAuth) {
130
- console.log(`🔐 Adding Auth templates...`);
130
+ console.log(` Adding Auth templates...`);
131
131
  // Need to ensure directories exist
132
132
  fs.mkdirSync(path.join(projectPath, 'src', 'controllers'), { recursive: true });
133
133
  fs.mkdirSync(path.join(projectPath, 'src', 'middlewares'), { recursive: true });
@@ -147,13 +147,33 @@ async function init() {
147
147
  path.join(projectPath, 'src', 'routes', 'auth.routes.js')
148
148
  );
149
149
 
150
- // Append JWT secret to env example
151
- fs.appendFileSync(path.join(projectPath, '.env.example'), '\nJWT_SECRET=supersecretjwtkey123\n');
152
- fs.appendFileSync(path.join(projectPath, '.env'), '\nJWT_SECRET=supersecretjwtkey123\n');
150
+ // Append JWT secret and Salt Rounds to env example
151
+ fs.appendFileSync(path.join(projectPath, '.env.example'), '\nJWT_SECRET=supersecretjwtkey123\nBCRYPT_SALT=10\n');
152
+ fs.appendFileSync(path.join(projectPath, '.env'), '\nJWT_SECRET=supersecretjwtkey123\nBCRYPT_SALT=10\n');
153
+
154
+ const utilsPath = path.join(projectPath, 'src', 'utils');
155
+ if (!fs.existsSync(utilsPath)) {
156
+ fs.mkdirSync(utilsPath, { recursive: true });
157
+ }
158
+ fs.writeFileSync(
159
+ path.join(utilsPath, 'hash.util.js'),
160
+ `import bcrypt from "bcryptjs";
161
+
162
+ export const hashData = async (data, saltRounds = process.env.BCRYPT_SALT) => {
163
+ const salt = await bcrypt.genSalt(Number(saltRounds) || 10);
164
+ return await bcrypt.hash(data, salt);
165
+ };
166
+
167
+ export const compareData = async (data, hashedData) => {
168
+ return await bcrypt.compare(data, hashedData);
169
+ };
170
+ `
171
+ );
172
+
153
173
  }
154
174
 
155
175
  if (initTests) {
156
- console.log(`🧪 Adding Jest test templates...`);
176
+ console.log(` Adding Jest test templates...`);
157
177
  fs.mkdirSync(path.join(projectPath, 'tests'), { recursive: true });
158
178
  fs.copyFileSync(
159
179
  path.join(__dirname, '..', 'templates', 'tests', 'healthcheck.test.js'),
@@ -225,7 +245,7 @@ async function init() {
225
245
  }
226
246
 
227
247
  // 3. Create package.json
228
- console.log(`📦 Setting up package.json...`);
248
+ console.log(` Setting up package.json...`);
229
249
  const packageJsonTemplate = {
230
250
  name: packageJsonName.trim(),
231
251
  version: "1.0.0",
@@ -279,7 +299,7 @@ async function init() {
279
299
 
280
300
  // Inject dependencies directly into package.json instead of doing them via raw arguments.
281
301
  // This perfectly bypasses PNPM / YARN / BUN specific registry caching bugs when downloading deeply nested trees.
282
- console.log(`\n Configuring ${packageManager} and resolving dependency trees...`);
302
+ console.log(`\n Configuring ${packageManager} and resolving dependency trees...`);
283
303
  const finalPackageJsonPath = path.join(projectPath, 'package.json');
284
304
  const finalPackageJsonCode = JSON.parse(fs.readFileSync(finalPackageJsonPath, 'utf8'));
285
305
 
@@ -294,12 +314,12 @@ async function init() {
294
314
 
295
315
  fs.writeFileSync(finalPackageJsonPath, JSON.stringify(finalPackageJsonCode, null, 2));
296
316
 
297
- console.log(`\n Running final installation via ${packageManager} (This might take a minute)...`);
317
+ console.log(`\n Running final installation via ${packageManager} (This might take a minute)...`);
298
318
  const installTriggerCmd = packageManager === 'npm' ? 'npm install' : `${packageManager} install`;
299
319
  execSync(installTriggerCmd, execConfig);
300
320
 
301
321
  if (initGit) {
302
- console.log(`\n🌱 Initializing Git repository...`);
322
+ console.log(`\n Initializing Git repository...`);
303
323
  execSync('git init', { cwd: projectPath, stdio: 'inherit' });
304
324
  // Create .gitignore
305
325
  const gitignoreContent = "node_modules\n.env\ndist\nbuild\ncoverage\n";
@@ -308,7 +328,7 @@ async function init() {
308
328
  execSync('git commit -m "initial commit"', { cwd: projectPath, stdio: 'inherit' });
309
329
  }
310
330
 
311
- console.log(`\n Success! Created "${projectName}" at ${projectPath}`);
331
+ console.log(`\n Success! Created "${projectName}" at ${projectPath}`);
312
332
  console.log('\nInside that directory, you can run several commands:');
313
333
  console.log(`\n ${packageManager === 'npm' ? 'npm run' : packageManager} dev`);
314
334
  console.log(' Starts the development server on localhost.');
@@ -318,11 +338,11 @@ async function init() {
318
338
  console.log(`\n cd ${projectName}`);
319
339
  console.log(` ${packageManager === 'npm' ? 'npm run' : packageManager} dev\n`);
320
340
  } catch (err) {
321
- console.error('\n❌ Failed to install dependencies. You may need to install them manually inside the folder.', err);
341
+ console.error('\nFailed to install dependencies. You may need to install them manually inside the folder.', err);
322
342
  }
323
343
  }
324
344
 
325
345
  init().catch(err => {
326
- console.error('\n❌ Unexpected error occurred:', err);
346
+ console.error('\nUnexpected error occurred:', err);
327
347
  process.exit(1);
328
348
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-express-kickstart",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Production-ready CLI starter for Express APIs",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {