How to Log HTTP Request Input and Response Body in NodeJS

Jeremy Ling
Geek Culture
Published in
2 min readMar 14, 2022

When deploying an API to production, it is crucial to be able to log all incoming requests to and responses from your server for reasons such as debugging, audit and optimisation. For NodeJS, there are a number of popular logging libraries that do an excellent job in setting you up with a base configuration. However, none of them do come with default request input and response body logging. Here’s a short tutorial on how we can achieve this with morgan.

In order to log additional information beyond morgan’s default configurations, we’ll need to create custom tokens. Let’s start with a custom middleware written to extend morgan.

// /middlewares/morgan.tsimport { Request, Response } from "express";
import morgan from "morgan";
// in my app, I extend express's Request interface to store a
// `requester` prop. this holds the decoded jwt token data
morgan.token("requester", function getRequester(req: Request): string {
return JSON.stringify(req.requester);
});
morgan.token("input", function getInput(req: Request): string {
let input: Record<string, any> = {};
if (req.method === "GET") {
input = req.query;
} else {
input = req.body;
}

// mask any input that should be secret
input = { ...input };
if (input.password) {
input.password = "*";
}

return JSON.stringify(input);
});
morgan.token("response-body", (req: Request, res: Response): string => {
const body = { ...JSON.parse(res.responseBody) };
// mask any input that should be secret
if (body?.data?.accessToken) {
body.data.accessToken = "*";
}
if (body?.data?.refreshToken) {
body.data.refreshToken = "*";
}

return JSON.stringify(body);
});
export { morgan };

We can now import this in index.ts and define a custom format with our newly defined tokens that morgan should use.

// index.tsimport express, { Express, Request, Response } from "express";
import { morgan } from "./middlewares/morgan";
const PORT = process.env.PORT || 3000;
const app: Express = express();
// override send to store response body for morgan token to retrieve
const originalSend = app.response.send;
app.response.send = function sendOverride(body) {
this.responseBody = body;
return originalSend.call(this, body);
};
app.use(express.json());
app.use(cookieParser()); // I use this to retrieve jwt from cookies
app.use(
morgan(
':requester :remote-addr [:date[clf]] ":method :url HTTP/:http-version" Input :input Response :response-body'
)
);
... // other middleware, route handlers, server setup

Finally, we’ll also need to extend the Request and Response types from express to avoid build-time errors. Make sure to include this file in your tsconfig.json within the include or files option.

// custom.d.tsdeclare namespace Express {
export interface Request {
requester: import("./models/Policy").Requester;
}

export interface Response {
responseBody: any;
}
}

We now have the entire request input and response body objects stored in every request log!

--

--

Jeremy Ling
Geek Culture

📋 Founder at https://formblob.com. 👻 Serial wannapreneur. 📚 Full-time philomath.