Skip to content

Debugging Express Applications

Even a well-tested Express app eventually needs debugging in the moment, a route behaving unexpectedly, a hanging request, a mysterious 500 error. This lesson covers the tools and techniques for finding out what's actually happening.

Request Logging as a First Line of Defense

morgan is the most common HTTP request logger for Express, printing method, path, status code, and response time for every request, an essential first signal when something isn't behaving as expected.

Combined with structured console.error() (or a real logger, covered in the Logging lesson) around error-handling middleware, request logs are often enough to pinpoint which route and status code is involved before digging further.

const morgan = require('morgan');
app.use(morgan('dev'));

// GET /users 200 12.345 ms - 128
// POST /users 400 3.201 ms - 42

The 'dev' format is colorized and concise, ideal for local development; use 'combined' for more detailed production-style logs.

Using Node's Built-In Inspector

node --inspect server.js
node --inspect-brk server.js  # pauses on the first line

# then open chrome://inspect in Chrome, or attach your editor's debugger
  • --inspect exposes a debugging port you can attach Chrome DevTools or an IDE debugger to.
  • --inspect-brk pauses execution immediately, useful for catching startup-time bugs.
  • Set breakpoints directly in your editor (most support Node debugging natively) instead of scattering console.log() everywhere.
  • The debug npm module lets you enable namespaced debug output via the DEBUG environment variable.

Debugging Cheatsheet

Common debugging tools and when to reach for each.

Tool Best For
morgan Quick visibility into every request's method, path, and status
console.error(err.stack) Immediate error visibility in error-handling middleware
node --inspect Step-through debugging with breakpoints
debug module Namespaced, toggleable debug logging via DEBUG=app:*
npm run dev + nodemon Fast iteration loop while diagnosing an issue

Diagnosing a Hanging Request

A request that never resolves almost always traces back to a missing next() call somewhere in the middleware chain, or a promise that's never awaited or resolved. Add temporary logging at the start of every middleware suspected in the chain to see exactly where execution stops.

app.use((req, res, next) => {
  console.log('logger middleware entered');
  next();
});

app.use((req, res, next) => {
  console.log('auth middleware entered'); // if this never logs, the previous middleware is the culprit
  next();
});

Using the debug Module

The debug module (which Express itself uses internally) lets you sprinkle namespaced debug statements throughout your code that stay silent by default, and can be selectively enabled via an environment variable without touching code.

const debug = require('debug')('app:orders');

exports.createOrder = async (data) => {
  debug('creating order with data: %o', data);
  // ...
};

Run with DEBUG=app:orders node server.js to see just that namespace's output, or DEBUG=app:* for everything.

Common Mistakes

  • Leaving stray console.log() statements scattered through production code instead of using a namespaced logger.
  • Debugging a hanging request by guessing instead of adding step-by-step logging through the middleware chain.
  • Not enabling request logging (morgan) at all in development, missing an easy first signal.
  • Reaching for a full debugger before trying simpler, faster tools like targeted logging.

Key Takeaways

  • morgan gives quick visibility into every request's method, path, status, and timing.
  • Node's built-in --inspect flag enables real breakpoint debugging via Chrome DevTools or your editor.
  • The debug module supports toggleable, namespaced logging without permanent console.log() clutter.
  • Hanging requests almost always trace back to a missing next() or an unresolved promise.

Pro Tip

When a request hangs with no error and no response, add a log statement to the very first line of every middleware in the suspected chain, the last one to log before it stalls tells you exactly where to look.