Method to assign unique IDS to express API requests for tracing

by SkillAiNest

The ability to detect what happens with API applications is an important aspect of monitoring, tracing and debugging back and applications. But how do you distinguish between two API applications notifications at the same API closing point?

The purpose of this article is to tell you how:

  • Properly assign API requests to your Express applications, properly,

  • Store and access the ID by using AsynClocalstorage API in Node Dot J, and

  • Use it in application logging.

Along with this guide, it will be helpful to make API’s closing points and use middleware in the express as you follow you. You can also apply the ideas to the framework like Nest JS and AA from this article.

The table of content

Starting with Starter Repeatory

To make it easier to follow, I have developed a starter project and hosted it on the Gut Hub. You can Clone it from here. Install the dependence on your local computer using your preferred Javascript Package Package Manager (NPM, Yarn, PNPM, Bin) to run and run it on your local computer. Then start the application by running npm start Command in the project terminal.

If the application begins successfully, it should log in to the bottom piece to the terminal:

Listening on port 3333

The application is currently just an API closing point. GET / . When you use API at the closing point using curl Or by visiting the browser you will get the “OK” string as a payload response:

$ curl -i 

OK%

If the above piece is what you see, congratulations! You have set the project correctly.

Ligger Utilites

The first step is to set customs lags for login messages in the terminal. The Lugars will log in the events that have arisen during the API application process and log in to the application summary.

You will need to install two Express middleware to achieve this goal. Moangan And Winston – Use your preferred package manager. If you use npmYou can run the command down the project folder in the terminal:

$ npm install morgan winston

If the above command is successful, Morgan and Winston will be added to it dependencies In objection package.json. Create a file that has a name logger.js In the project route folder. logger.js Customs will consist of a code for the utility.

The first ligar utility that you will make loggerWinston is prepared from the package you have previously installed. logger Is the item with two ways:



const winston = require("winston");

const  = winston.format;

const logHandler = winston.createLogger({
  level: "debug",
  levels: winston.config.npm.levels,
  format: combine(
    timestamp(), 
    errors( "500"),
      content_length: tokens.res(req, res, "content-length") + " bytes",
      response_time: Number(tokens("response-time")(req, res) ),
    json(),
    colorize({
      all: true,
      colors: {
        info: "gray", 
        error: "red", 
        },
    })
  ),
  transports: (new winston.transports.Console()),
});

exports.logger = {
  info: function (message) {
    logHandler.child({}).info(message);
  },

  error: function (message) {
    logHandler.child({}).error(message);
  },
};

In a piece of code mentioned above, winston.createLogger Used to create logHandler. logger Is exported out of logger.js Module and logger.info And logger.error Are the functions that use logHandler Log in messages in the terminal.

The second Logar Utility will be a middleware that will log in to the application before sending information about the application. This will log information, such as how long it took to run the application and the application status code. It will be called logRequestSummary And Morgan will package and use http Method logHandler.



const winston = require("winston");
const morgan = require("morgan");

const { combine, errors, json, timestamp, colorize } = winston.format;

const logHandler = winston.createLogger({
  
  format: combine(
    
    colorize({
      all: true,
      colors: {
        
        http: "blue", 
      },
    })
  ),
  
});

exports.logger = {
    
};

exports.logRequestSummary = morgan(
  function (tokens, req, res) {
    return JSON.stringify({
      url: tokens.url(req, res),
      method: tokens.method(req, res),
      status_code: Number(tokens.status(req, res) || "500"),
      content_length: tokens.res(req, res, "content-length") + " bytes",
      response_time: Number(tokens("response-time")(req, res) || "0") + " ms",
    });
  },
  {
    stream: {
      
      write: (message) => {
        const httpLog = JSON.parse(message);
        logHandler.http(httpLog);
      },
    },
  }
);

JSON string returned by the first function when morgan The function is hanged write The job of stream In the second argument, the function of the object was transferred to the function. Then he was passed to JSON and moved logHandler.http To be logged in with winston.npm http Level of intensity.

At this point, two items are recovered from logger.js: logger And logRequestSummary.

I index.jsCreate a new controller to handle GET To the requests /hello Even import and use the way exported items logger.js. Use logger Information to log in when events occur in controllers and add logRequestSummary As a middleware for application.


const express = require("express");
const { logRequestSummary, logger } = require("./logger");

const app = express();

app.use(
  express.json(),
  express.urlencoded({ extended: true }),
  logRequestSummary 
);

app.get("/", function (req, res) {
  logger.info(`${req.method} request to ${req.url}`); 
  return res.json({ method: req.method, url: req.url });
});

app.get("/hello", function (req, res) {
  logger.info(`${req.method} request to ${req.url}`); 
  return res.json({ method: req.method, url: req.url });
});


Stop the application (with CTRL + C Or OPTION + C), And resume with her npm start. Make API applications at the closing locations of both APIs, you will see output like a piece below in the terminal – an event log and summary of the application summary.

{
  "level": "info",
  "message": "GET request to /",
  "timestamp": "2025-08-16 10:35:06.831 PM"
}
{
  "level": "http",
  "message": {
    "content_length": "26 bytes",
    "method": "GET",
    "response_time": "9.034 ms",
    "status_code": 200,
    "url": "/"
  },
  "timestamp": "2025-08-16 10:35:06.844 PM"
}

You can see the latest condition of the code 2-custom-logger-middleware Using the Branch git checkout 2-custom-logger-middleware Or by visiting the branch 2-Custom-Logar-Medalware Of storage

Now that you are able to log and view the events that occur for each API application, how do you distinguish between two consecutive requests at the same closing point? How do you know which API application logged in a specific message? How do you explain the API request when you talk to your fellow fellow? By attaching a unique ID to each request, you will be able to answer all these questions.

What is AsynClocalstorage and why is it important?

Ago AsynClocalstorageInformation related to application context in Express Storage users res.locals The object with asyncalstorage, node. The JS provides a local way to store information that is necessary to perform contradictory functions. According to its documents, it is a performance and memory -stored implementation that includes significant improvements that will be difficult for you to implement yourself.

When you use AsynClocalstorage, you can store and access information in the same way Local storage In the browser you have passed the Store Value (usually a item, but it may also be an ancient value) as a first argument and a contradictory function that should be accessed by another argument when you process it. run Method

James Steel, one of the leading partners of Node.com JS, explain this in this video further ASYNC Local Storage API with ASYNC Context to track in the node with API.

Store the application ID in AsynClocalstorage

In the project, create a file with the name context-storage.js. In this file, you will exemplify and export it asyncalstorage (if these are not yet configured). This example of the Asniccoal storage will be used in storing and recovering application identities for any other context, which requires the identification of the application.


const { AsyncLocalStorage } = require("node:async_hooks");

let store;

module.exports.contextStorage = function () {
  if (!store) {
    store = new AsyncLocalStorage();
  }

  return store;
};

You will create another file called set-request-id.js Which will build and export a middleware. Middleware stops API requests, will produce an ID of application, and will store it asyncalstorage for example context-storage.js If it does not already exist.

You can use your desired ID -making library, but we’ll use here randomUUID From nod dot j crypto Package


const { randomUUID } = require("node:crypto");
const { contextStorage } = require("./context-storage");


module.exports.setRequestId = function () {
  return function (_req, _res, next) {
    requestId = randomUUID();
    const store = contextStorage().getStore();

    if (!store) {
      return contextStorage().run({ requestId }, next);
    }

    if (store && !store.requestId) {
      store.requestId = requestId;
      return next();
    }

    return next();
  };
};

I setRequestId Function in the above piece, examples of asyniccoal storage context-storage.js Has been recovered from the return price of the execution contextStorage As if store. Unless store Is wrong, run Executes the procedure next Express callback, providing requestId For access to anywhere in anything next By contextStorage.

Unless store It has a value but not requestId Property, Set requestId Property and its price and execution on next Ceremony

Finally, the place setRequestId As the first middleware of the Express application index.js So that each application can identify the identity before conducting another operation.


const express = require("express");
const { logRequestSummary, logger } = require("./logger");
const { setRequestId } = require("./set-request-id");

const app = express();

app.use(
  setRequestId(), 
  express.json(),
  express.urlencoded({ extended: true }),
  logRequestSummary
);


You can check out the current state of this project if you run git checkout 3-async-local-storage-req-id Command on your terminal or by visiting 3-eSync-Local-Storage-Req-Eid Of the Gut Hub

Use the application ID in Lagar’s utility

Now that requestId Placed in the property store, you can access it from anywhere next Using contextStorage. You will have access to the functions in it logger.js And connect it to the logs so that when the messages for an application are logged into the terminal, the application ID will appear with the login message.


const winston = require("winston");
const morgan = require("morgan");
const { contextStorage } = require("./context-storage");

const { combine, errors, json, timestamp, colorize } = winston.format;

const logHandler = winston.createLogger({
  level: "debug",
  levels: winston.config.npm.levels,
  format: combine(

    
    winston.format((info) => {
      info.request_id = contextStorage().getStore()?.requestId;
      return info;
    })(),
    

    timestamp({ format: "YYYY-MM-DD hh:mm:ss.SSS A" }),
    errors({ stack: true }),
    
  ),
  transports: (new winston.transports.Console()),
});


I combine Function from Winston, you will add a function argument that accepts the login message. info – as an argument and attach request_id Property for this. Its value of value requestId Retrieved from contextStorage. With this amendment, any message login for application will be the ID of the application associated with this application.

With this complete, stop running the project if it is already running and running it again npm start Order. Make API applications to two closing locations and you will see outpatted like a bottom piece on the terminal:

{
  "level": "info",
  "message": "GET request to /hello",
  "request_id": "c80e92d0-eafe-42c7-b093-e5ffce014819",
  "timestamp": "2025-08-17 07:58:13.571 PM"
}
{
  "level": "http",
  "message": {
    "content_length": "31 bytes",
    "method": "GET",
    "response_time": "9.397 ms",
    "status_code": 200,
    "url": "/hello"
  },
  "request_id": "c80e92d0-eafe-42c7-b093-e5ffce014819",
  "timestamp": "2025-08-17 07:58:13.584 PM"
}

Unlike the previous logout, it contains ID for each application. Using AsyncLocalStorage Application ID value effectively and using it in Ligers to access it, you can properly find login messages on their API requests.

If you drive you can access the current state of the project git checkout 4-use-context-in-logger Command on the terminal or by visiting 4 in use context Of the Gut Hub

You have been able to store, access and connect the request identification with its login message. Can you compose a request as a header on reply? The challenge is to set the header, X-Request-IdOn the answer, so that the request is worth the identification of the application in response to each request. X-Request-Id Answer Header.

It is useful to communicate with Front End when trying to debugging requests.

Conclusion

When API applications can be monitored, you can track performance matrix to discover areas that require improvement and attention, failed requests and server errors and because it has happened, and the application of the application for the planning and scalebuability in the volume matrix.

When you attach a unique identifier with an API application, you can use it to detect the events that occur within the application life and separate it from other similar requests.

In addition to using Asyncalstorage to store application identities, you can also use it to store other application information, such as authentic user details. The use of Asyncalstorage to store information related to application context is considered an excellent process.

You may also like

Leave a Comment

At Skillainest, we believe the future belongs to those who embrace AI, upgrade their skills, and stay ahead of the curve.

Get latest news

Subscribe my Newsletter for new blog posts, tips & new photos. Let's stay updated!

@2025 Skillainest.Designed and Developed by Pro