I'm building an API Gateway using Node.js, Express, and http-proxy-middleware. All incoming requests are forwarded to different services based on dynamic routing logic.
I'm encountering a 408 Request Timeout from the target server. What’s confusing is that the proxy seems to hang before the onProxyReq hook even runs, as my logs stop right before that.
EDIT
No more 408 Request Timeout. But onProxyReq still not triggered.
Turns out the request body is not being sent, hence the hanging and 408 status. To make sure request body is being sent instead of being swallowed/missing, I use req.rawBody + streamifier.createReadStream().
In my app.js
//get the raw body data
app.use(async (req, res, next) => {
if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
try {
req.rawBody = await getRawBody(req);
if (req.headers['content-type']?.includes('application/json')) {
req.body = JSON.parse(req.rawBody.toString());
}
} catch (err) {
return res.status(400).send('Invalid JSON body');
}
}
next();
});
//my proxy function is inside this
app.use(dynamicRouter);
//dynamicRouter function
const dynamicRouter = (req, res, next) => {
const entrypoint = req.path;
const clientId = req.body?.client;
if (!clientId || !req.rawBody) {
return res.status(400).json({ error: 'Missing client_id or raw body' });
}
// Example route
const proxyRoute = {
endpointPath: '/api/test',
targetUrl: 'https://example.com'
};
if (entrypoint === proxyRoute.endpointPath) {
return handleProxyRequest(proxyRoute, req, res, next);
}
return res.status(404).json({ message: 'Route not found' });
};
const handleProxyRequest = (route, req, res, next) => {
const { targetUrl, endpointPath } = route;
console.log(`[GATEWAY] Forwarding ${req.path} to ${targetUrl}${endpointPath}`);
console.log("Request Body:", req.body);
const middleware = createProxyMiddleware({
target: targetUrl,
changeOrigin: true,
logLevel: 'debug',
pathRewrite: () => endpointPath,
buffer: streamifier.createReadStream(req.rawBody),
onProxyReq: (proxyReq, req, res) => {
console.log('✅ onProxyReq is being called');
// send JSON body manually
if (
req.method === 'POST' &&
req.headers['content-type']?.includes('application/json') &&
req.body
) {
const bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
}
// Log
console.log('Forwarding:', req.method, req.originalUrl);
},
onProxyRes: (proxyRes, req) => {
console.log('onProxyRes is being called');
},
onError: (err, req, res) => {
console.error('Proxy error:', err.message);
if (!res.headersSent) {
res.status(502).json({ error: 'Bad Gateway' });
}
}
});
return middleware(req, res, next);
};
I see the log: console.log("Request Body:", req.body); But I never see the onProxyReq log even when the target API returns the correct response.
Can someone help me or point me to the right direction? Thank you in advance!
Here's what I've tried
- Req.body is populated correctly (I use express.json before this runs)
- targetUrl and endpointPath are valid and point to a reachable API (based on log :"[GATEWAY] Forwarding ..."
- req.method is POST, and Content-Type is application/json
my logs stops here: console.log("Request Body:", req.body);
and no log for console.log('✅ onProxyReq is being called');
dynamicRouteris defined? You've gotreturn middleware(req, res, next)but that doesn't appear to be in a function nor arereq,resornextdefined