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
httpmodule — 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:
| Capability | Raw http | Express |
|---|---|---|
| Routing | Manual if/else on req.url and req.method | Declarative app.get(), app.post(), etc. |
| Middleware | Manual function chaining | Built-in app.use() pipeline |
| Body parsing | Read stream manually, JSON.parse() | express.json() one-liner |
| Response helpers | res.writeHead() + res.end() | res.json(), res.send(), res.status() |
| Static files | Manual file reading with fs | express.static() |
| Query parsing | Manual url.parse() or URL API | req.query auto-parsed |
| Route params | Manual regex or string parsing | req.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
- Declarative routing —
app.get('/users/:id', handler)instead of string matching - Middleware pipeline — reusable functions that process every request (logging, auth, CORS)
- Body parsing — one line:
app.use(express.json()) - Response utilities —
res.json(),res.status(404).send(),res.redirect() - Query & param parsing —
req.query,req.paramsauto-populated - Static file serving —
express.static('public')replaces manualfs.readFile - 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:
| Metric | Express |
|---|---|
| npm weekly downloads | 30+ million |
| GitHub stars | 65,000+ |
| First release | 2010 |
| Used by | IBM, Uber, Twitter, Accenture, many startups |
| Middleware packages | Thousands (cors, helmet, morgan, passport, etc.) |
Why it stays dominant
- Simplicity — small learning curve, quick to prototype
- Maturity — 15+ years of battle-tested production use
- Ecosystem — virtually every Node tutorial and library assumes Express
- Flexibility — scales from a 10-line demo to large enterprise APIs
- Community — answers for almost every question on Stack Overflow
5. Express vs other Node.js frameworks
| Framework | Philosophy | Performance | Learning Curve | Best For |
|---|---|---|---|---|
| Express | Minimal, unopinionated | Good | Low | APIs, web apps, prototypes |
| Fastify | Performance-first, schema-based | Excellent (2-3x Express in benchmarks) | Medium | High-throughput APIs |
| Koa | Modern Express (by same author), async/await-first | Good | Medium | Developers wanting cleaner middleware |
| Hapi | Configuration-driven, enterprise | Good | High | Enterprise apps, strict validation |
| NestJS | Opinionated, Angular-inspired, TypeScript-first | Good (uses Express or Fastify underneath) | High | Large 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
| Feature | Express 4.x | Express 5.x (beta) |
|---|---|---|
| Status | Stable, production-ready | Beta / release candidate |
| Promise support in handlers | Must catch errors manually | Rejected promises auto-forwarded to error handler |
req.query | Configurable parser | Consistent, more predictable |
app.del() | Deprecated alias for app.delete() | Removed |
req.host | Includes port | Returns hostname only |
| Path route matching | Uses path-to-regexp v1 | Uses path-to-regexp v8 (stricter) |
| Minimum Node version | Node 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
- Express is a minimal web framework that wraps Node's
httpmodule with routing, middleware, and response helpers. - It solves the verbosity and complexity of writing servers with raw
http— declarative routes, automatic parsing, clean response API. - Express is unopinionated — you choose the structure, ORM, and tools.
- Everything flows through middleware functions:
(req, res, next). - Express 4.x is the current stable version; 5.x adds async error handling and stricter routing.
- Despite newer alternatives like Fastify and NestJS, Express remains the most popular Node.js framework by far.
Explain-It Challenge
Explain without notes:
- Name three things Express gives you that the raw
httpmodule does not. - What does it mean that Express is "unopinionated"? Give one advantage and one disadvantage.
- In one sentence, describe the middleware pattern that Express is built on.
- When might you choose Fastify or NestJS over Express?
Navigation: <- 3.4 Overview | 3.4.b — Setting Up Express Server ->