Node.js and Express Backend Development Handbook

Express Framework Fundamentals

The Node.js web framework provides a structured pipeline from top to bottom:

  • Routing system
  • Middleware pipeline
  • Response utilities
  • Template integration (Pug, etc.)

The standard flow follows: Request → Middleware → Route → Response.

Middleware Pipeline

Middleware functions run before the final response. The function signature is (req, res, next). These functions can:

  • Modify the req or res objects.
  • Stop the request using res.send().
  • Pass control using next().

Note: If neither next() nor res.send() is called, the request will hang. next() continues the flow, while res.send() ends the request lifecycle.

Routing and Request Handling

Routes are defined using app.METHOD(PATH, handler):

  • app.get(): Read data
  • app.post(): Send data
  • app.put(): Update data
  • app.delete(): Remove data

Route Priority Rules

Dynamic routes like /items/:id use parameters. A :param can match anything unless a more specific route is placed first in the code.

The Request and Response Objects

The Request object (req) contains incoming client data:

  • req.params: URL parameters (e.g., /users/:id)
  • req.query: Query strings (e.g., ?id=10)
  • req.body: POST/PUT data (requires middleware). Note: GET requests usually do not have a body.

The Response object (res) is used to send output:

  • res.send(): Sends text or HTML.
  • res.json(): Sends JSON data.
  • res.render(): Compiles a Pug template into HTML.

Static Files and Pug Templates

Use app.use(express.static('public')) to serve CSS, images, and JS directly. This bypasses routes if a file exists.

Pug is a server-side template engine that converts .pug files to HTML. It runs only on the server. The rendering flow is: Route → res.render() → Pug compiles → HTML → Browser.

Pug Conditionals

Example: if user, p Welcome; else, p Login. These are evaluated on the server, so only one branch is sent to the client.

Asynchronous Programming and Node.js

Execution Order

The execution priority follows: Synchronous → Microtasks → Macrotasks.

Example pattern: console.logPromise.thensetTimeout. Promises always run before setTimeout, even with a 0ms delay.

Promises and Async/Await

Promises have three states: pending, fulfilled, or rejected. Note: “Resolved” is not a formal technical state. A throw statement results in a rejection, and a .catch() block does not stop the chain.

Async/Await allows async functions to return a Promise and await to pause execution inside that function. This is not automatic parallel execution.

The Node.js Runtime Model

Node.js uses a single thread, an event loop, and non-blocking I/O. I/O tasks are delegated rather than blocked, but CPU parallelism is not automatic.

Core Modules and NPM

  • HTTP Server: Created via http.createServer. Missing res.end() causes the request to hang.
  • URL Module: Used to parse req.url, which is not parsed automatically.
  • FS Module: fs.promises.readFile() is asynchronous, while fs.readFileSync() blocks execution.
  • Path Module: Handles cross-platform file paths using join and resolve.

NPM Versioning: The ^ symbol (e.g., "^4.17.1") allows minor and patch updates but prevents major version upgrades.

Database Integration with MongoDB

NoSQL vs Relational Databases

MongoDB is a NoSQL database that is non-relational and schema-less. Data is stored as BSON (JSON-like documents). The structure consists of Database → Collection → Document.

Mongoose ODM

Mongoose provides structure via Schemas and Models. A Model is the interface for a collection; Mongoose automatically pluralizes the model name (e.g., “Item” becomes “items”).

CRUD Operations

  • Create: const newItem = new Item({ name: "Book" }); await newItem.save();
  • Read: Item.find(), Item.findOne(), or Item.findById().
  • Update: Item.updateOne() or Item.updateMany() using operators like $inc.
  • Delete: Item.deleteOne() or Item.deleteMany().

Modular Routing

To avoid a massive server.js file, use express.Router() to separate features into different files and attach them using app.use('/path', router).

User Authentication and Security

Authentication vs Authorization

Authentication verifies identity (login), while Authorization determines permissions. Authentication must always occur first.

Session-Based Authentication

The server creates a session and stores the Session ID in a client cookie. The session data stays on the server. Sessions are temporary and end upon logout or browser closure.

Token-Based Authentication (JWT)

This is a stateless method where the client stores a token and sends it with each request. JSON Web Tokens (JWT) are signed but not encrypted; the payload is readable via Base64, but integrity is maintained via the signature.

Password Security

Always use bcrypt for hashing. Hashing is a one-way process; never store plain-text passwords. Hash is not the same as encryption because it cannot be reversed.

Deployment Considerations

When deploying to platforms like Render:

  • Use a GitHub repository.
  • Set the start command to node app.js.
  • Use process.env.PORT || 3000 for port binding.
  • Ensure node_modules and .env files are excluded from the upload.