Skip to content

Aaditya-Kumar-Mittal/backend_Professional-Level-Project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

30 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ backend_Professional-Level-Project

Node.js Express.js MongoDB Mongoose JWT bcrypt dotenv Multer Cookie-Parser CORS Cloudinary Prettier Nodemon


πŸ“ Folder Structure

src/
β”‚
β”œβ”€β”€ config/              # Configuration files (DB, Cloudinary, etc.)
β”œβ”€β”€ constants.js         # App constants
β”œβ”€β”€ controllers/         # Route controllers
β”œβ”€β”€ middleware/          # Custom middlewares
β”œβ”€β”€ models/              # Mongoose models
β”œβ”€β”€ routes/              # Express routes
β”œβ”€β”€ utils/               # Utility functions
β”œβ”€β”€ database/            # DB connection logic
β”œβ”€β”€ constants.js        # App constants
β”œβ”€β”€ app.js               # Express app
└── index.js             # Entry point

Note: Empty folders are tracked using .gitkeep files. These ensure structure is preserved in Git.


πŸ“¦ Installation & Setup

git clone https://github.com/your-username/backend_Professional-Level-Project.git
cd backend_Professional-Level-Project
npm install
cp .env.example .env

βš™οΈ Scripts

"scripts": {
Β  "dev": "nodemon -r dotenv/config --experimental-json-modules src/index.js"
}

Use the development script to run the server with environment variables auto-loaded.


πŸ” Environment Variables

Environment-specific secrets and configurations are placed inside a .env file.

MONGODB_URI=your_mongodb_uri
PORT=8000
CORS_ORIGIN=http://localhost:3000

ACCESS_TOKEN_SECRET=your_access_token
REFRESH_TOKEN_SECRET=your_refresh_token
ACCESS_TOKEN_EXPIRATION=15m
REFRESH_TOKEN_EXPIRATION=7d

CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

You can generate strong JWT secrets using:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

βœ… What is asyncHandler?

In Express.js, asynchronous route handlers that throw errors (e.g., failed DB operations) don’t automatically trigger the error-handling middleware. That’s where asyncHandler comes in.

It is a higher-order function (a function that returns another function) that helps:

  • Automatically catch errors from async functions.
  • Forward them to the default Express error handler via next(error).
  • Avoid repetitive try...catch blocks in each route or controller.

🧠 Breakdown of the Original Code

const asyncHandler = (fn) => async (req, res, next) => {
  try {
    await fn(req, res, next);
  } catch (error) {
    res.status(error.code || 500).json({
      success: false,
      message: error.message || "Internal Server Error",
      error: error,
    });
    next(error);
  }
};

⚠️ Fix: Change err.code to error.code.


βœ… Improved asyncHandler with Comments

/**
 * Wraps an async route/controller function and forwards any errors to Express error handler
 * Avoids boilerplate try-catch blocks across the app.
 */
const asyncHandler = (fn) => {
  return async (req, res, next) => {
    try {
      await fn(req, res, next);
    } catch (error) {
      console.error("Error caught in asyncHandler:", error);

      res.status(error.code || 500).json({
        success: false,
        message: error.message || "Something went wrong",
        error: process.env.NODE_ENV === "development" ? error : {},
      });

      next(error);
    }
  };
};

export { asyncHandler };

πŸ”„ Centralized Error Handling with Custom Error Class

// utils/ErrorHandler.js

class ErrorHandler extends Error {
  constructor(message, code) {
    super(message);
    this.code = code; // Maintains proper stack trace

    Error.captureStackTrace(this, this.constructor);
  }
}

export default ErrorHandler;

Use in routes like:

throw new ErrorHandler("User not found", 404);

🧠 JSON Web Tokens (JWT)

JWT (JSON Web Token) is a stateless authentication mechanism consisting of:

  • Header (Algorithm & Type)
  • Payload (user data)
  • Signature (secured with secret)

πŸ”‘ Secret Key Generator:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Set it in .env:

ACCESS_TOKEN_SECRET=your_generated_secret
REFRESH_TOKEN_SECRET=your_refresh_token_secret

πŸ“ Tokens are Bearer tokens β€” the one who bears it has access to resources.


♻️ Access & Refresh Tokens

πŸ”Έ Access Token

  • Short-lived (e.g., 15 min)
  • Used in API headers to authenticate users

πŸ”Έ Refresh Token

  • Long-lived (e.g., 7 days)
  • Used to issue new access tokens
  • Stored securely (HTTP-only cookies recommended)
ACCESS_TOKEN_EXPIRATION=15m
REFRESH_TOKEN_EXPIRATION=7d

☁️ File Upload in Production

❗ Avoid Local File Storage in Production

Use cloud storage services like:

  • AWS S3
  • Cloudinary
  • Google Cloud Storage
  • Azure Blob Storage

πŸ›  Popular Libraries

  • multer β†’ Used to parse multipart/form-data for file uploads
  • cloudinary β†’ Used for image/file hosting and transformation

Example Cloudinary Config:

cloudinary.config({
  cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});

πŸ“¦ mongoose-aggregate-paginate-v2

A pagination plugin for Mongoose aggregation pipelines.

πŸ”§ Setup

import mongoose from "mongoose";
import aggregatePaginate from "mongoose-aggregate-paginate-v2";

const MySchema = new mongoose.Schema({
  name: String,
  age: Number,
});

MySchema.plugin(aggregatePaginate);
export default mongoose.model("User", MySchema);

πŸ” Usage (Promise)

const options = { page: 1, limit: 10 };
const aggregate = User.aggregate();

User.aggregatePaginate(aggregate, options)
  .then((results) => console.log(results))
  .catch((err) => console.error(err));

🧠 Usage (Callback)

User.aggregatePaginate(aggregate, options, (err, results) => {
  if (err) return console.error(err);
  console.log(results);
});

🧰 Developer Utilities

βš™οΈ Prettier Setup

Install:

npm i -D prettier

Create .prettierrc:

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5"
}

βš™οΈ Nodemon Script

"scripts": {
Β  "dev": "nodemon -r dotenv/config --experimental-json-modules src/index.js"
}

Production Grade Multer and Cloudinary Setup

This section explains how to handle file uploads in a production-grade Node.js application using Multer for local storage and Cloudinary for cloud storage. The system first stores the uploaded file temporarily on the server and then uploads it to Cloudinary. This approach offers better fault tolerance, allowing for retries in case of upload errors.

πŸ—‚οΈ Project Flow

  1. User uploads a file via a client (form or API call).
  2. Multer stores the file temporarily in a specified local folder.
  3. The server uploads the file from local storage to Cloudinary.
  4. On success, the Cloudinary URL is saved in the database (optional).
  5. The local file can be optionally deleted after successful upload.

βš™οΈ Setup Multer with diskStorage

const multer = require("multer");
const path = require("path");

// Configure local storage
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploads/"); // Make sure this folder exists
  },
  filename: function (req, file, cb) {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    const ext = path.extname(file.originalname); // Preserve original extension
    cb(null, file.fieldname + "-" + uniqueSuffix + ext);
  },
});

const upload = multer({ storage: storage });

βœ… Note: Multer does not create directories when using a custom destination function. Ensure the uploads/ folder exists beforehand.

☁️ Upload to Cloudinary

const cloudinary = require("cloudinary").v2;
const fs = require("fs");

// Configure cloudinary
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.CLOUD_API_KEY,
  api_secret: process.env.CLOUD_API_SECRET,
});

// Upload from local storage to cloudinary
const uploadToCloudinary = async (localFilePath) => {
  try {
    const result = await cloudinary.uploader.upload(localFilePath, {
      folder: "your-folder-name",
    });

    // Optional: delete local file after successful upload
    fs.unlinkSync(localFilePath);
    return result;
  } catch (error) {
    throw new Error("Cloudinary upload failed");
  }
};

πŸ§ͺ Express Route Example

const express = require("express");
const router = express.Router();

router.post("/upload", upload.single("file"), async (req, res) => {
  try {
    const localPath = req.file.path;
    const cloudinaryResponse = await uploadToCloudinary(localPath);
    res.status(200).json({
      message: "File uploaded successfully",
      cloudinaryUrl: cloudinaryResponse.secure_url,
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

πŸ›‘οΈ Why This Approach is Production-Grade?

  • Redundancy: Local backup enables retry on failed cloud uploads.
  • Traceability: Files are stored with unique names and extensions.
  • Security: Files are not stored permanently on the server.
  • Scalability: Upload logic is modular and reusable.

✨ Features & Best Practices

  • βœ… Organized codebase inside src folder.

  • βœ… MongoDB connected via Mongoose with proper async-await & try-catch for DB security.

  • βœ… Uses dotenv for environment configuration.

  • βœ… Integrates JWT for secure auth (access & refresh tokens).

  • βœ… Image/file handling using Multer and Cloudinary.

  • βœ… CORS enabled for secure cross-origin access:

    app.use(cors({ origin: process.env.CORS_ORIGIN }));
  • βœ… Limits large incoming JSON:

    app.use(express.json({ limit: "16kb" }));
  • βœ… Cookie support via:

    import cookieParser from "cookie-parser";
    app.use(cookieParser());
  • βœ… Uses Prettier for consistent formatting:

    • Install: npm i -D prettier

    • Create .prettierrc:

      {
        "semi": true,
        "singleQuote": true,
        "tabWidth": 2,
        "trailingComma": "es5"
      }
  • βœ… Follows production security: DB access IP-limited in MongoDB Atlas.

    • βœ… Keep .env secure and gitignored
  • βœ… Use .gitkeep to track empty folders

  • βœ… Modularize code into src with subfolders

  • βœ… Use cors for restricted API access

  • βœ… Apply size limits to incoming requests:

    app.use(express.json({ limit: "16kb" }));
  • βœ… Store access & refresh token expiry values in .env

  • βœ… Restrict MongoDB Atlas access to IP ranges (never 0.0.0.0/0 in production)

  • βœ… Always catch async DB errors in routes or use asyncHandler


Standardization and HTTP Headers

This section provides a comprehensive overview of web standards around HTTP, including the purpose of headers, common types, security practices, CORS configuration, HTTP methods with examples, and standard response codes.

πŸ”— URL, URI, and URN β€” What's the Difference?

Term Full Form Description Example
URL Uniform Resource Locator Specifies the location of a resource https://example.com/index.html
URI Uniform Resource Identifier Identifies a resource either by name or location (or both) https://example.com, urn:isbn:0451450523
URN Uniform Resource Name Names the resource without indicating its location urn:isbn:0451450523

🧾 HTTP Headers Metadata

Headers are key-value pairs sent with HTTP requests/responses. They provide metadata to help the client or server understand how to process the data.

They are used for:

  • πŸ” Authentication
  • βš™οΈ Managing state
  • 🧠 Caching
  • 🧩 Content Negotiation

πŸ“¦ Deprecated X- Prefix

Before 2012, custom headers often started with X- (e.g. X-Custom-Token). Since then, the standard has deprecated the use of X- prefixes in favor of meaningful names.

Example:

X-Powered-By: Express  // ❌ Deprecated pattern
Server: Express         // βœ… Modern pattern

πŸ”€ Types of HTTP Headers

  • Request Headers: Sent by the client (browser or Postman).
  • Response Headers: Sent by the server (Express, Django, etc.).
  • Representation Headers: Describe the encoding or compression.
  • Payload Headers: Describe the body data being transferred.

🧰 Most Common HTTP Headers

Header Description
Accept Tells the server what content types the client can process. Example: application/json
User-Agent Identifies the application making the request (e.g., Chrome, Postman)
Authorization Carries credentials for authentication (e.g., Bearer Token)
Content-Type Describes the format of the request/response body
Cookie Contains stored session or preference information
Cache-Control Controls how caching is handled by the browser or proxy

🌐 CORS: Cross-Origin Resource Sharing

Cross-Origin Resource Sharing allows restricted resources on a web page to be requested from another domain.

Header Purpose
Access-Control-Allow-Origin Specifies the origin(s) allowed
Access-Control-Allow-Credentials Allows sending of cookies and auth headers
Access-Control-Allow-Methods Lists allowed HTTP methods (e.g., GET, POST)

πŸ›‘οΈ Security Headers

Header Purpose
Cross-Origin-Embedder-Policy Prevents unauthorized resources from being embedded
Cross-Origin-Opener-Policy Isolates browsing context for improved security
Content-Security-Policy Prevents XSS attacks by restricting sources
X-XSS-Protection Activates browser’s XSS protection mechanisms

βš™οΈ HTTP Methods with Real-Life Examples

Method Description Example
GET Retrieve a resource GET /users β€” Fetch all users
HEAD Fetch only headers HEAD /users β€” Get metadata only
OPTIONS Discover supported methods OPTIONS /users β€” Returns: GET, POST
TRACE Debug, echoes back request Rarely used in modern APIs
DELETE Remove a resource DELETE /users/123 β€” Delete user with ID 123
PUT Replace a resource PUT /users/123 with full user object
POST Create a new resource POST /users with user data
PATCH Modify part of a resource PATCH /users/123 with name update

Example (Express.js POST endpoint):

app.post("/users", (req, res) => {
  const { name, email } = req.body;
  // Save to DB
  res.status(201).json({ message: "User created" });
});

πŸ“Š HTTP Response Status Codes

Status codes indicate the result of an HTTP request. They are grouped into five categories:

Class Description
1xx Informational (e.g., 100 Continue)
2xx Success (e.g., 200 OK, 201 Created)
3xx Redirection (e.g., 307 Temporary Redirect)
4xx Client Errors (e.g., 404 Not Found) (Client Did Not Send Correct Information)
5xx Server Errors (e.g., 500 Internal Server Error)

βœ… Common Status Codes

Code Meaning
100 Continue
102 Processing
200 OK
201 Created
202 Accepted
307 Temporary Redirect
308 Permanent Redirect
400 Bad Request
401 Unauthorized
402 Payment Required
404 Not Found
500 Internal Server Error
504 Gateway Timeout

⚠️ Notes for Production

  • ❌ Never push .env or secrets β€” use .gitignore to exclude them.
  • βœ… In MongoDB Atlas, restrict access to your server IP only.
  • βœ… Place all DB connections in try-catch blocks to avoid crashes.
  • βœ… Serve secure headers with packages like helmet.

πŸ“˜ What's Next?

To take your backend development to the next level:

  • πŸ— System Design
  • πŸ—„οΈ Database Design
  • πŸš€ Code Optimization
  • 🧩 Debugging & Profiling

πŸ“š Resources


πŸ“ License

This project is open source and available under the MIT License.


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors