create-express-kickstart 1.0.0 → 1.0.1

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.
@@ -0,0 +1,8 @@
1
+ PORT=8000
2
+ MONGODB_URI=mongodb://localhost:27017/
3
+ CORS_ORIGIN=*
4
+ NODE_ENV=development
5
+
6
+ # Rate Limiting
7
+ RATE_LIMIT_WINDOW_MS=900000 # 15 minutes in milliseconds
8
+ RATE_LIMIT_MAX=100 # Maximum requests per windowMs
@@ -0,0 +1,22 @@
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
+ }
@@ -0,0 +1,75 @@
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 };
@@ -0,0 +1,17 @@
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 };
@@ -0,0 +1,14 @@
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;
@@ -0,0 +1,37 @@
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 };
@@ -0,0 +1,18 @@
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
+ });
@@ -0,0 +1,9 @@
1
+ import { Router } from 'express';
2
+ import { healthcheck, triggerError } from '#controllers/healthcheck.controller.js';
3
+
4
+ const router = Router();
5
+
6
+ router.route('/').get(healthcheck);
7
+ router.route('/error').get(triggerError);
8
+
9
+ export default router;
@@ -0,0 +1,27 @@
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
+ });
@@ -0,0 +1,23 @@
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 }
@@ -0,0 +1,10 @@
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 }
@@ -0,0 +1,7 @@
1
+ const asyncHandler = (requestHandler) => {
2
+ return (req, res, next) => {
3
+ Promise.resolve(requestHandler(req, res, next)).catch((err) => next(err));
4
+ };
5
+ };
6
+
7
+ export { asyncHandler };
@@ -0,0 +1 @@
1
+ export const DB_NAME = "my_app_db";
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "create-express-kickstart",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Production-ready CLI starter for Express APIs",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
7
- "create-express-kickstart": "./bin/cli.js"
7
+ "create-express-kickstart": "bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -21,6 +21,5 @@
21
21
  "author": "Your Name",
22
22
  "license": "ISC",
23
23
  "type": "module",
24
- "dependencies": {
25
- }
24
+ "dependencies": {}
26
25
  }