Advanced Express.js: Overriding the API, Template Engines, Debugging, and Working Behind Proxies

Advanced Express.js: Overriding the API, Template Engines, Debugging, and Working Behind Proxies

Express API Overrideexpress.request / express.responseTemplate EnginesDebugging Expresstrust proxyReverse ProxyX-Forwarded-For

~3 min read • Updated Dec 26, 2025

1. Overriding the Express API


Express exposes many methods and properties on req and res. These are inherited through prototypes, which makes it possible to override or extend them.

There are two extension points:

  • Global level: express.request and express.response
  • App level: app.request and app.response

Global overrides affect all Express apps in the same process. App-level overrides affect only the specific application instance.

2. Overriding Methods


You can override any existing method by assigning a new function to it.

Example: Custom res.sendStatus

app.response.sendStatus = function (statusCode, type, message) {
  return this.contentType(type)
    .status(statusCode)
    .send(message)
}

Usage:

res.sendStatus(404, 'application/json', '{"error":"resource not found"}')

3. Overriding Properties


Express properties fall into two categories:

  • Assigned properties (e.g., req.baseUrl) — cannot be overridden.
  • Getter properties (e.g., req.ip) — can be overridden.

Example: Custom req.ip

Object.defineProperty(app.request, 'ip', {
  configurable: true,
  enumerable: true,
  get () { return this.get('Client-IP') }
})

This version derives the IP address from the Client-IP header.

4. Changing the Prototype


By default, Express uses:

  • http.IncomingRequest.prototype for requests
  • http.ServerResponse.prototype for responses

You can replace these prototypes if needed, but it is recommended to do so only at the application level.

Example:

Object.setPrototypeOf(Object.getPrototypeOf(app.request), FakeRequest.prototype)
Object.setPrototypeOf(Object.getPrototypeOf(app.response), FakeResponse.prototype)

5. Using Template Engines in Express


Template engines allow you to use static template files and inject dynamic values at runtime to generate HTML pages.

Express supports many engines:

  • Pug (default)
  • EJS
  • Handlebars
  • Any engine supported by @ladjs/consolidate

6. Configuring a Template Engine


In your app.js:

app.set('views', './views')
app.set('view engine', 'pug')

Install Pug:

npm install pug --save

7. Creating a Template File (Pug)


File: views/index.pug

html
  head
    title= title
  body
    h1= message

8. Rendering the Template


app.get('/', (req, res) => {
  res.render('index', { title: 'Hey', message: 'Hello there!' })
})

Visiting the home page renders the Pug template as HTML.

9. Template Engine Cache


The cache stores the compiled template, not the rendered output. The template is re-rendered on every request.

10. Debugging Express


Express uses the debug module internally. To enable all Express logs:

DEBUG=express:* node index.js

On Windows PowerShell:

$env:DEBUG = "express:*"; node index.js

You can target specific namespaces:

  • express:router
  • express:application
  • express:view

Multiple namespaces:

DEBUG=http,mail,express:* node index.js

Advanced DEBUG options

NamePurpose
DEBUGEnable/disable namespaces
DEBUG_COLORSEnable colored output
DEBUG_DEPTHObject inspection depth
DEBUG_FDFile descriptor for output
DEBUG_SHOW_HIDDENShow hidden object properties

11. Express Behind Reverse Proxies


When Express runs behind a reverse proxy, some APIs (like req.ip) may return the proxy’s IP instead of the client’s. The trust proxy setting adjusts this behavior.

trust proxy values:

  • Boolean — trust all or none
  • IP/Subnet — trust specific proxies
  • Number — trust a specific number of hops
  • Function — custom trust logic

Examples:

app.set('trust proxy', true)
app.set('trust proxy', 'loopback')
app.set('trust proxy', ['loopback', 'uniquelocal'])
app.set('trust proxy', 2)
app.set('trust proxy', ip => ip === '127.0.0.1')

Effects of enabling trust proxy:

  • req.hostname comes from X-Forwarded-Host
  • req.protocol comes from X-Forwarded-Proto
  • req.ip and req.ips use X-Forwarded-For

The implementation uses the proxy-addr package.

Conclusion


Express provides deep flexibility through prototype overrides, template engines, debugging tools, and proxy-aware configuration. Mastering these advanced features allows you to build scalable, customizable, and production-ready applications with full control over request handling and rendering.

Written & researched by Dr. Shahin Siami