create-express-kickstart 1.0.1 ā 1.0.2
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/cli.js +10 -5
- package/package.json +1 -1
- package/nodejs-app/.env.example +0 -8
- package/nodejs-app/package.json +0 -22
- package/nodejs-app/src/app.js +0 -75
- package/nodejs-app/src/controllers/healthcheck.controller.js +0 -17
- package/nodejs-app/src/db/index.js +0 -14
- package/nodejs-app/src/middlewares/errorHandler.middleware.js +0 -37
- package/nodejs-app/src/models/example-model.js +0 -18
- package/nodejs-app/src/routes/healthcheck.routes.js +0 -9
- package/nodejs-app/src/server.js +0 -27
- package/nodejs-app/src/utils/ApiError.js +0 -23
- package/nodejs-app/src/utils/ApiResponse.js +0 -10
- package/nodejs-app/src/utils/asyncHandler.js +0 -7
- package/nodejs-app/src/utils/constants.js +0 -1
package/bin/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
@@ -21,11 +21,11 @@ async function init() {
|
|
|
21
21
|
|
|
22
22
|
let projectName = projectNameArg;
|
|
23
23
|
if (!projectName) {
|
|
24
|
-
projectName = await question('\nš Project
|
|
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
|
|
28
|
+
console.error('\nā Error: Project Directory Name is required.');
|
|
29
29
|
process.exit(1);
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -33,10 +33,15 @@ 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 name.\n`);
|
|
36
|
+
console.error(`\nā Error: 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}): `);
|
|
41
|
+
if (!packageJsonName.trim()) {
|
|
42
|
+
packageJsonName = projectName; // Fallback to directory name
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
const description = await question('š Project description: ');
|
|
41
46
|
const author = await question('š Author name: ');
|
|
42
47
|
|
|
@@ -102,7 +107,7 @@ async function init() {
|
|
|
102
107
|
// 3. Create package.json
|
|
103
108
|
console.log(`š¦ Setting up package.json...`);
|
|
104
109
|
const packageJsonTemplate = {
|
|
105
|
-
name:
|
|
110
|
+
name: packageJsonName.trim(),
|
|
106
111
|
version: "1.0.0",
|
|
107
112
|
description: description || "A production-ready Node.js Express API",
|
|
108
113
|
main: "src/server.js",
|
package/package.json
CHANGED
package/nodejs-app/.env.example
DELETED
package/nodejs-app/package.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nodejs-app",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "description",
|
|
5
|
-
"main": "src/server.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"start": "node src/server.js",
|
|
9
|
-
"dev": "nodemon src/server.js",
|
|
10
|
-
"format": "prettier --write \"src/**/*.{js,json}\""
|
|
11
|
-
},
|
|
12
|
-
"imports": {
|
|
13
|
-
"#*": "./src/*"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"express",
|
|
17
|
-
"node",
|
|
18
|
-
"api"
|
|
19
|
-
],
|
|
20
|
-
"author": "aaaaaaaaaaa",
|
|
21
|
-
"license": "ISC"
|
|
22
|
-
}
|
package/nodejs-app/src/app.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import express from "express";
|
|
2
|
-
import cors from "cors";
|
|
3
|
-
import cookieParser from "cookie-parser";
|
|
4
|
-
import helmet from "helmet";
|
|
5
|
-
import pinoHttp from "pino-http";
|
|
6
|
-
import rateLimit from "express-rate-limit";
|
|
7
|
-
import { errorHandler } from "#middlewares/errorHandler.middleware.js";
|
|
8
|
-
|
|
9
|
-
// Import routers
|
|
10
|
-
import healthcheckRouter from "#routes/healthcheck.routes.js";
|
|
11
|
-
|
|
12
|
-
const app = express();
|
|
13
|
-
|
|
14
|
-
// Security HTTP headers
|
|
15
|
-
app.use(helmet());
|
|
16
|
-
|
|
17
|
-
// Rate Limiting
|
|
18
|
-
const limiter = rateLimit({
|
|
19
|
-
windowMs: process.env.RATE_LIMIT_WINDOW_MS || 15 * 60 * 1000, // Default 15 minutes
|
|
20
|
-
limit: process.env.RATE_LIMIT_MAX || 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
|
|
21
|
-
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
|
|
22
|
-
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
23
|
-
message: "Too many requests from this IP, please try again later"
|
|
24
|
-
});
|
|
25
|
-
app.use("/api", limiter); // Apply rate limiting to all API routes
|
|
26
|
-
|
|
27
|
-
// Logging
|
|
28
|
-
app.use(pinoHttp({
|
|
29
|
-
customLogLevel: function (req, res, err) {
|
|
30
|
-
if (res.statusCode >= 400 && res.statusCode < 500) {
|
|
31
|
-
return 'warn'
|
|
32
|
-
} else if (res.statusCode >= 500 || err) {
|
|
33
|
-
return 'error'
|
|
34
|
-
} else if (res.statusCode >= 300 && res.statusCode < 400) {
|
|
35
|
-
return 'silent'
|
|
36
|
-
}
|
|
37
|
-
return 'info'
|
|
38
|
-
},
|
|
39
|
-
// Dynamically require pino-pretty if in dev and it exists, else undefined
|
|
40
|
-
transport: process.env.NODE_ENV === "development" ? (function() {
|
|
41
|
-
try {
|
|
42
|
-
import("pino-pretty");
|
|
43
|
-
return {
|
|
44
|
-
target: 'pino-pretty',
|
|
45
|
-
options: { colorize: true }
|
|
46
|
-
};
|
|
47
|
-
} catch (e) {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
})() : undefined
|
|
51
|
-
}));
|
|
52
|
-
|
|
53
|
-
// CORS setup
|
|
54
|
-
app.use(
|
|
55
|
-
cors({
|
|
56
|
-
origin: process.env.CORS_ORIGIN || "*", // Fallback to allowing everything
|
|
57
|
-
credentials: true, // Allow cookies with requests
|
|
58
|
-
})
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// Payload sizes and forms
|
|
62
|
-
app.use(express.json({ limit: "16kb" }));
|
|
63
|
-
app.use(express.urlencoded({ extended: true, limit: "16kb" }));
|
|
64
|
-
app.use(express.static("public"));
|
|
65
|
-
app.use(cookieParser());
|
|
66
|
-
|
|
67
|
-
// -------- API ROUTES ---------
|
|
68
|
-
// Mount routers
|
|
69
|
-
app.use("/api/v1/healthcheck", healthcheckRouter);
|
|
70
|
-
|
|
71
|
-
// Global Error Handler
|
|
72
|
-
// Always add this as the very last middleware
|
|
73
|
-
app.use(errorHandler);
|
|
74
|
-
|
|
75
|
-
export { app };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ApiError } from "#utils/ApiError.js";
|
|
2
|
-
import { ApiResponse } from "#utils/ApiResponse.js";
|
|
3
|
-
import { asyncHandler } from "#utils/asyncHandler.js";
|
|
4
|
-
|
|
5
|
-
const healthcheck = asyncHandler(async (req, res) => {
|
|
6
|
-
// Basic health check
|
|
7
|
-
return res
|
|
8
|
-
.status(200)
|
|
9
|
-
.json(new ApiResponse(200, { status: "OK", timestamp: Date.now() }, "App is running smoothly"));
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const triggerError = asyncHandler(async (req, res) => {
|
|
13
|
-
// Dummy route to test the global error handler
|
|
14
|
-
throw new ApiError(400, "This is a custom error thrown for testing purposes.");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
export { healthcheck, triggerError };
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import mongoose from "mongoose";
|
|
2
|
-
import { DB_NAME } from "#utils/constants.js";
|
|
3
|
-
|
|
4
|
-
const connectDB = async () => {
|
|
5
|
-
try {
|
|
6
|
-
const connectionInstance = await mongoose.connect(`${process.env.MONGODB_URI}/${DB_NAME}`);
|
|
7
|
-
console.log(`\n MongoDB connected !! DB HOST: ${connectionInstance.connection.host}`);
|
|
8
|
-
} catch (error) {
|
|
9
|
-
console.error("MONGODB connection FAILED ", error);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default connectDB;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { ApiError } from '#utils/ApiError.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Global Error Handler Middleware
|
|
5
|
-
* @param {Error} err
|
|
6
|
-
* @param {Request} req
|
|
7
|
-
* @param {Response} res
|
|
8
|
-
* @param {NextFunction} next
|
|
9
|
-
*/
|
|
10
|
-
const errorHandler = (err, req, res, next) => {
|
|
11
|
-
let error = err;
|
|
12
|
-
|
|
13
|
-
// If the error is not an instance of ApiError, transform it into one
|
|
14
|
-
if (!(error instanceof ApiError)) {
|
|
15
|
-
const statusCode = error.statusCode ? error.statusCode : 500;
|
|
16
|
-
const message = error.message || "Internal Server Error";
|
|
17
|
-
|
|
18
|
-
error = new ApiError(
|
|
19
|
-
statusCode,
|
|
20
|
-
message,
|
|
21
|
-
error?.errors || [], // Pass down any validation errors
|
|
22
|
-
err.stack // Keep the original stack trace
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Now format the consistent response
|
|
27
|
-
const response = {
|
|
28
|
-
...error,
|
|
29
|
-
message: error.message,
|
|
30
|
-
...(process.env.NODE_ENV === 'development' ? { stack: error.stack } : {})
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// Send the JSON response
|
|
34
|
-
return res.status(error.statusCode).json(response);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export { errorHandler };
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//example-model.js
|
|
2
|
-
import mongoose from "mongoose";
|
|
3
|
-
|
|
4
|
-
const exampleSchema = new mongoose.Schema({
|
|
5
|
-
name: {
|
|
6
|
-
type: String,
|
|
7
|
-
required: [true, "Name is required"],
|
|
8
|
-
},
|
|
9
|
-
age: {
|
|
10
|
-
type: Number,
|
|
11
|
-
required: [true, "Age is required"],
|
|
12
|
-
},
|
|
13
|
-
email: {
|
|
14
|
-
type: String,
|
|
15
|
-
required: [true, "Email is required"],
|
|
16
|
-
unique: true,
|
|
17
|
-
},
|
|
18
|
-
});
|
package/nodejs-app/src/server.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import dotenv from "dotenv";
|
|
2
|
-
import { app } from "#app.js";
|
|
3
|
-
|
|
4
|
-
// Load environment variables from .env file
|
|
5
|
-
dotenv.config({
|
|
6
|
-
path: './.env'
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
import connectDB from "#db/index.js";
|
|
10
|
-
|
|
11
|
-
const PORT = process.env.PORT || 8000;
|
|
12
|
-
|
|
13
|
-
connectDB()
|
|
14
|
-
.then(() => {
|
|
15
|
-
app.listen(PORT, () => {
|
|
16
|
-
console.log(`Server is running at port : ${PORT}`);
|
|
17
|
-
});
|
|
18
|
-
})
|
|
19
|
-
.catch((err) => {
|
|
20
|
-
console.log("MONGO db connection failed !!! ", err);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
process.on("unhandledRejection", (err) => {
|
|
24
|
-
console.log("UNHANDLED REJECTION! Shutting down...");
|
|
25
|
-
console.log(err.name, err.message);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
class ApiError extends Error {
|
|
2
|
-
constructor(
|
|
3
|
-
statusCode,
|
|
4
|
-
message = "Something went wrong",
|
|
5
|
-
errors = [],
|
|
6
|
-
stack = ""
|
|
7
|
-
) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.statusCode = statusCode;
|
|
10
|
-
this.data = null;
|
|
11
|
-
this.message = message;
|
|
12
|
-
this.success = false;
|
|
13
|
-
this.errors = errors;
|
|
14
|
-
|
|
15
|
-
if (stack) {
|
|
16
|
-
this.stack = stack;
|
|
17
|
-
} else {
|
|
18
|
-
Error.captureStackTrace(this, this.constructor);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export { ApiError }
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
class ApiResponse {
|
|
2
|
-
constructor(statusCode, data, message = "Success") {
|
|
3
|
-
this.statusCode = statusCode;
|
|
4
|
-
this.data = data;
|
|
5
|
-
this.message = message;
|
|
6
|
-
this.success = statusCode < 400; // Success is true if status code is not an error level
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export { ApiResponse }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const DB_NAME = "my_app_db";
|