Skip to content

Express.js vs Node.js

"Express vs Node" is a common but slightly misleading comparison, Express runs on top of Node, not instead of it. This lesson shows the same server written both ways so you can see exactly what Express adds.

Node's Built-In HTTP Module

Node.js ships with an http module capable of creating a web server with zero external dependencies. It works at a very low level: you get a raw request stream and must parse the URL, method, and body yourself.

Express is built on top of this exact module. Calling express() internally creates the same kind of server Node's http.createServer() would, but wraps it with routing, middleware, and a richer req/res API.

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

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/users') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify([{ id: 1, name: 'Ada' }]));
    return;
  }
  res.writeHead(404);
  res.end('Not found');
});

server.listen(3000);

Every route needs a manual if check on method and URL, and every response needs manual headers and JSON.stringify().

The Same Server in Express

const express = require('express');
const app = express();

app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Ada' }]);
});

app.listen(3000);
  • app.get('/users', ...) replaces the manual method === 'GET' && url === '/users' check.
  • res.json() replaces writeHead + JSON.stringify + end.
  • Adding a second route in raw Node means adding another if branch; in Express it's another app.get() call.
  • Both versions run on the exact same underlying Node HTTP server.

Node http vs Express Cheatsheet

A side-by-side look at common tasks in both approaches.

Task Raw Node.js Express.js
Create server http.createServer(fn) express() + app.listen()
Route matching Manual if (req.url === ...) app.get('/path', fn)
Route params Manual regex/string parsing app.get('/users/:id', fn)
Send JSON res.end(JSON.stringify(x)) res.json(x)
Set status res.writeHead(404) res.status(404)
Middleware Not built in app.use(fn)

What Express Actually Adds

Express does not replace anything Node provides, it adds a layer of conveniences on top: declarative routing, the middleware pipeline, route parameters, and a req/res API with dozens of helper methods (res.redirect, res.cookie, res.status, req.params, req.query, and more).

  • Declarative routing instead of manual URL string comparisons.
  • Composable middleware for cross-cutting concerns (logging, auth, parsing).
  • Route parameters and query string parsing built in.
  • A large ecosystem of drop-in middleware (helmet, cors, morgan, multer, and more).

When You Might Skip Express

For extremely simple scripts, a single health-check endpoint, or performance-critical microservices where every dependency is scrutinized, some teams use raw http or a lighter framework. For the vast majority of real applications and APIs, the small overhead Express adds is far outweighed by the productivity and ecosystem it provides.

Common Mistakes

  • Believing Express is a separate runtime from Node.js, it is an npm package that runs inside Node.
  • Thinking you must choose one or the other; Express code is still ordinary Node.js code.
  • Assuming raw http servers are always faster in a way that matters for typical CRUD APIs.
  • Rewriting Express conveniences (like route params) manually instead of using the built-in support.

Key Takeaways

  • Express is built on top of Node's http module, not a replacement for it.
  • Express trades a small amount of overhead for routing, middleware, and a richer API.
  • The same server written in raw Node requires far more manual parsing and branching.
  • Almost all typical web apps and REST APIs benefit more from Express's productivity than they lose in raw performance.

Pro Tip

If you ever need to debug "what is Express actually doing here", try writing the same tiny route in raw http first, it demystifies what the framework is doing under the hood.