Express Response Modification Middleware

raw JSON →
1.1.0 verified Thu Apr 23 auth: no javascript

The `modify-response-middleware` package provides an Express middleware designed to intercept and transform HTTP response bodies before they are sent to the client. It transparently handles various compression encodings like Gzip, Brotli, Deflate, and Br, allowing developers to modify response data regardless of its original compression status. As of version 1.1.0, it supports both uncompressed and compressed payloads, offering options to disable caching (`noCache`). This middleware is particularly useful for injecting data, sanitizing output, or performing last-minute transformations on API responses. Its release cadence appears stable but infrequent, typical for a focused utility. A key differentiator is its built-in handling of common compression algorithms, abstracting away the complexities of decompression and re-compression during modification.

error SyntaxError: Unexpected token < in JSON at position 0
cause Attempting to `JSON.parse()` a non-JSON response, often an HTML error page or another content type, because the `Content-Type` header was not checked.
fix
Before parsing, check res.get('Content-Type') within your modifyRes callback to ensure it's application/json. Add a try...catch block around JSON.parse() to handle non-JSON or malformed responses gracefully.
error TypeError: content.toString is not a function
cause The `content` parameter passed to your callback is `null`, `undefined`, or not a `Buffer` instance, often for empty responses or if upstream middleware unexpectedly altered the body format.
fix
Always add a check if (content && Buffer.isBuffer(content)) before calling content.toString() or attempting to process content. For empty responses, content might legitimately be null or an empty buffer, in which case it should be returned as is.
error (Client-side) ERR_CONTENT_LENGTH_MISMATCH or truncated responses
cause The `Content-Length` header sent by the server does not match the actual byte length of the modified response body, causing client-side errors or incomplete downloads.
fix
When the response body is modified, the middleware should ideally reset or remove the Content-Length header. If issues persist, ensure the middleware or your callback removes res.removeHeader('Content-Length') or explicitly sets res.setHeader('Content-Length', Buffer.byteLength(newContent)) for non-compressed content.
breaking Middleware order is critical. `modify-response-middleware` must be applied *before* any route handlers or other middleware that send responses (e.g., `res.send()`, `res.json()`). Placing it after these handlers will result in it never being invoked for those responses, as headers might have already been sent.
fix Ensure `app.use(modifyRes(...))` is declared early in your Express application's middleware stack, ideally before route definitions. If using router-level middleware, place it before route-specific handlers within that router.
gotcha Modifying response bodies, especially after decompression and before re-compression, can introduce performance overhead. For very large responses or high-traffic applications, this can significantly impact server throughput and latency due to CPU usage for (de)compression and memory for buffering.
fix Profile your application with and without the middleware in production scenarios. Consider applying the middleware only to specific routes (using `app.use('/path', modifyRes(...))`) or conditionally based on response size if performance becomes an issue.
gotcha When modifying response content, `Content-Length` and `ETag` headers can become invalid. The `noCache: true` option helps mitigate `ETag` issues by setting `Cache-Control: no-cache`, but if `Content-Length` is present and incorrect, it can lead to client-side issues or truncated responses. The middleware attempts to handle this, but explicit verification is sometimes needed.
fix If `noCache` is not suitable, ensure your modified response handler explicitly removes or recalculates `Content-Length` and `ETag` headers if the body size changes significantly. Monitor network responses to ensure headers align with modified content.
gotcha The callback function provided to `modifyRes` expects `content` to be a `Buffer`. If your modification logic assumes a specific content type (e.g., JSON) and attempts parsing without proper checks, it can throw errors for other content types (e.g., HTML, plain text, images).
fix Always validate the content type (e.g., `res.get('Content-Type')`) within your `modifyRes` callback before attempting type-specific parsing (e.g., `JSON.parse()`). Wrap parsing logic in `try...catch` blocks to handle malformed or unexpected data gracefully. Return the original `content` buffer if parsing/modification fails.
npm install modify-response-middleware
yarn add modify-response-middleware
pnpm add modify-response-middleware

This quickstart demonstrates how to integrate `modify-response-middleware` into an Express application to intercept and modify JSON response bodies before they are sent to the client.

import express from 'express';
import modifyRes from 'modify-response-middleware';

const app = express();

app.use(express.json()); // Ensure JSON body parsing for incoming requests if needed

// Apply the modification middleware early in the stack
app.use(modifyRes((content, req, res) => {
  if (content && res.get('Content-Type')?.includes('application/json')) {
    try {
      const data = JSON.parse(content.toString());
      // Example: Modify the age property of the JSON response
      data.age = (data.age || 0) + 2;
      data.message = 'Response modified by middleware!';
      return Buffer.from(JSON.stringify(data));
    } catch (e) {
      console.error('Failed to parse or modify JSON content:', e);
      // Return original content if modification fails
      return content;
    }
  }
  // Always return content, even if no modification occurred or type didn't match
  return content;
}, {
  noCache: true, // Prevents 304 Not Modified responses, useful for dynamic content
}));

app.get('/user', (req, res) => {
  res.json({
    name: 'Tom',
    age: 18,
    status: 'active'
  });
});

app.get('/html', (req, res) => {
  res.send('<h1>Hello, World!</h1><p>This is an HTML response.</p>');
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
  console.log(`Test with: curl http://localhost:${PORT}/user`);
  console.log(`Test with: curl http://localhost:${PORT}/html`);
});