Episode 3 — NodeJS MongoDB Backend Architecture / 3.4 — Express JS

3.4.a — What is Express.js?

In one sentence: Express.js is a minimal, unopinionated web framework for Node.js that provides a thin layer of routing, middleware, and response utilities on top of the built-in http module — making it the de facto standard for building web servers and APIs in the Node ecosystem.

Navigation: <- 3.4 Overview | 3.4.b — Setting Up Express Server ->


1. What is Express.js?

Express.js (commonly just Express) is a web application framework for Node.js. It wraps Node's native http module with a cleaner, more productive API:

// Raw Node.js http module — verbose
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World');
  } else if (req.method === 'GET' && req.url === '/about') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About Page');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

server.listen(3000);
// Same thing with Express — clean, declarative
const express = require('express');
const app = express();

app.get('/', (req, res) => res.send('Hello World'));
app.get('/about', (req, res) => res.send('About Page'));

app.listen(3000);

Express does not replace Node's http module. It sits on top of it, giving you:

CapabilityRaw httpExpress
RoutingManual if/else on req.url and req.methodDeclarative app.get(), app.post(), etc.
MiddlewareManual function chainingBuilt-in app.use() pipeline
Body parsingRead stream manually, JSON.parse()express.json() one-liner
Response helpersres.writeHead() + res.end()res.json(), res.send(), res.status()
Static filesManual file reading with fsexpress.static()
Query parsingManual url.parse() or URL APIreq.query auto-parsed
Route paramsManual regex or string parsingreq.params from /users/:id

2. Why Express over the raw http module

The raw http module is fine for understanding how Node handles network connections, but it becomes painful for real applications:

Pain points of raw http

// Pain #1: Manual routing grows into spaghetti
if (req.method === 'GET' && req.url === '/api/users') { /* ... */ }
else if (req.method === 'POST' && req.url === '/api/users') { /* ... */ }
else if (req.method === 'GET' && req.url.startsWith('/api/users/')) { /* ... */ }
// 50 more routes...

// Pain #2: Manual body parsing
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
  const parsed = JSON.parse(body);
  // now use parsed...
});

// Pain #3: Manual content-type handling
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'ok' }));

// Pain #4: No middleware pipeline — cross-cutting concerns (logging, auth)
// must be manually wired into every handler

What Express solves

  1. Declarative routingapp.get('/users/:id', handler) instead of string matching
  2. Middleware pipeline — reusable functions that process every request (logging, auth, CORS)
  3. Body parsing — one line: app.use(express.json())
  4. Response utilitiesres.json(), res.status(404).send(), res.redirect()
  5. Query & param parsingreq.query, req.params auto-populated
  6. Static file servingexpress.static('public') replaces manual fs.readFile
  7. Ecosystem — thousands of middleware packages on npm

3. Express philosophy

Express follows a clear set of design principles:

Minimal

Express provides the bare essentials. It does not force you into a specific project structure, ORM, template engine, or authentication strategy. You choose what you need.

Unopinionated

Unlike frameworks like NestJS or Ruby on Rails, Express makes few decisions for you. There is no "right way" to organize files. This is both a strength (flexibility) and a weakness (no guardrails for beginners).

Middleware-based

Everything in Express flows through middleware — functions with the signature (req, res, next). Logging? Middleware. Authentication? Middleware. Body parsing? Middleware. Even your route handlers are technically middleware.

// This IS middleware
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // pass to next middleware
});

// Route handlers are middleware that end the cycle
app.get('/', (req, res) => {
  res.send('Home'); // no next() — response sent
});

Flexible

Express works for:

  • Simple REST APIs
  • Server-rendered web apps (with template engines like EJS, Pug)
  • GraphQL servers
  • Proxy servers
  • Microservices
  • Full-stack apps serving static front-end builds

4. Express popularity and ecosystem

Express remains the most downloaded Node.js web framework by a wide margin:

MetricExpress
npm weekly downloads30+ million
GitHub stars65,000+
First release2010
Used byIBM, Uber, Twitter, Accenture, many startups
Middleware packagesThousands (cors, helmet, morgan, passport, etc.)

Why it stays dominant

  1. Simplicity — small learning curve, quick to prototype
  2. Maturity — 15+ years of battle-tested production use
  3. Ecosystem — virtually every Node tutorial and library assumes Express
  4. Flexibility — scales from a 10-line demo to large enterprise APIs
  5. Community — answers for almost every question on Stack Overflow

5. Express vs other Node.js frameworks

FrameworkPhilosophyPerformanceLearning CurveBest For
ExpressMinimal, unopinionatedGoodLowAPIs, web apps, prototypes
FastifyPerformance-first, schema-basedExcellent (2-3x Express in benchmarks)MediumHigh-throughput APIs
KoaModern Express (by same author), async/await-firstGoodMediumDevelopers wanting cleaner middleware
HapiConfiguration-driven, enterpriseGoodHighEnterprise apps, strict validation
NestJSOpinionated, Angular-inspired, TypeScript-firstGood (uses Express or Fastify underneath)HighLarge enterprise apps, teams from Angular

When Express is the right choice

  • You want fast prototyping and a huge ecosystem
  • Your team knows Express (most Node developers do)
  • You need flexibility in architecture decisions
  • The project is an API or server-rendered app of moderate complexity

When to consider alternatives

  • Fastify — if raw throughput matters and you want built-in schema validation
  • NestJS — if you want enforced structure, dependency injection, and TypeScript decorators
  • Koa — if you prefer a modern, lighter core with native async/await patterns

6. Installing Express

Express is installed via npm like any other package:

# Initialize a new Node.js project (if not done yet)
mkdir my-express-app
cd my-express-app
npm init -y

# Install Express
npm install express

After installation, your package.json will include:

{
  "dependencies": {
    "express": "^4.21.0"
  }
}

And node_modules/express/ contains the framework plus its dependencies.

Verify installation

// test.js
const express = require('express');
console.log('Express version:', require('express/package.json').version);
// Output: Express version: 4.21.0 (or similar)

7. Express versioning: 4.x vs upcoming 5.x

FeatureExpress 4.xExpress 5.x (beta)
StatusStable, production-readyBeta / release candidate
Promise support in handlersMust catch errors manuallyRejected promises auto-forwarded to error handler
req.queryConfigurable parserConsistent, more predictable
app.del()Deprecated alias for app.delete()Removed
req.hostIncludes portReturns hostname only
Path route matchingUses path-to-regexp v1Uses path-to-regexp v8 (stricter)
Minimum Node versionNode 0.10+Node 18+

What this means for you

  • Learn Express 4.x — it is what runs in production everywhere today
  • Know that 5.x is coming — the biggest win is automatic async error handling
  • Your Express 4 knowledge transfers directly — the API is nearly identical
// Express 4: must wrap async handlers manually
app.get('/users', async (req, res, next) => {
  try {
    const users = await db.getUsers();
    res.json(users);
  } catch (err) {
    next(err); // must call next(err) manually
  }
});

// Express 5: async errors auto-forwarded
app.get('/users', async (req, res) => {
  const users = await db.getUsers();
  res.json(users);
  // if getUsers() rejects, Express 5 catches it automatically
});

8. The Express request-response cycle (preview)

Every Express application follows this flow:

Client sends HTTP request
        |
        v
Express receives request (creates req, res objects)
        |
        v
Middleware pipeline runs (top to bottom)
  - express.json() parses body
  - custom logger
  - auth check
        |
        v
Route handler matches (app.get, app.post, etc.)
        |
        v
Handler sends response (res.json, res.send, etc.)
        |
        v
Client receives HTTP response

You will see each piece of this cycle in detail throughout the remaining subtopics.


9. Key takeaways

  1. Express is a minimal web framework that wraps Node's http module with routing, middleware, and response helpers.
  2. It solves the verbosity and complexity of writing servers with raw http — declarative routes, automatic parsing, clean response API.
  3. Express is unopinionated — you choose the structure, ORM, and tools.
  4. Everything flows through middleware functions: (req, res, next).
  5. Express 4.x is the current stable version; 5.x adds async error handling and stricter routing.
  6. Despite newer alternatives like Fastify and NestJS, Express remains the most popular Node.js framework by far.

Explain-It Challenge

Explain without notes:

  1. Name three things Express gives you that the raw http module does not.
  2. What does it mean that Express is "unopinionated"? Give one advantage and one disadvantage.
  3. In one sentence, describe the middleware pattern that Express is built on.
  4. When might you choose Fastify or NestJS over Express?

Navigation: <- 3.4 Overview | 3.4.b — Setting Up Express Server ->