DEV Community

Cover image for Handling Audit Logging in Node.js
mwangiKibui
mwangiKibui

Posted on

Handling Audit Logging in Node.js

Audit logs are records on the system that provide evidence of the sequence of activities that have been done at a given time.They help in monitoring the series of activities been done.

In this article, we will be going through step by step process of handling audit logs in Node.js. 

Pre-requisites:

Before proceeding in this article, it is good to have the following:

Overview:

Setting up the application:

  • Proceed to your preferred working directory.
  • From the terminal, initialize an NPM project using the default configurations:
  npm init -y
Enter fullscreen mode Exit fullscreen mode
  • We will be using the below packages:

    • Express.js: For setting up the web server.
    • Mongoose: ORM, for connecting to MongoDB.
    • Dotenv: For loading environmental variables.

Install the packages by running this command:

  npm run install express mongoose dotenv
Enter fullscreen mode Exit fullscreen mode
  • Once the packages, have been installed, the next step is to set up the entry point file of the application.
  • In the project directory, create a file named app.js which will be the entry point file.
  • In the app.js file:

    • Start by importing the necessary packages:
    const express = require('express');
    const dotenv = require('dotenv');
    const {join} = require('path');
    
    • Define the path where .env file will be:
    dotenv.config({ path: join(__dirname,'.env') });
    
    • Initialize the application server:
    const app = express();
    
    • Accept JSON payloads for POST requests:
    app.use(express.json());
    
    • Set up dummy GET, POST, and PUT requests:
    // GET
    app.get("/", (req,res) => res.json({
       success:true,
       message:"Get request sent successfully"
    }));
    
    // POST
    app.post("/", (req,res) => res.json({
       success:true,
       message:"Post request sent successfully"
    }));
    
    // PUT
    app.put("/:pet", (req,res) => res.json({
       success:true,
       message:"Put request sent successfully"
    }));
    

    From the above routes, we are simply returning a JSON response. We will use the routes to derive the audit logs.

    • Start the application server:
    app.listen(process.env.PORT,() => {
        console.log("Server started and running on port "+process.env.PORT)
    });
    

Since we are already calling an environmental variable, it is about time we create a .env file and specify the port.

In the project directory, create a .env file. In it, specify the PORT as below:

PORT=5000
Enter fullscreen mode Exit fullscreen mode

The next step is to set up a model that will be holding the audit logs data.

Setting up the models

For our case, we will only have one model AuditTrail. In this model, we will store the following:

  • The URL.
  • The activity / name of action been done.
  • The request params.
  • The request query.
  • The request payload.
  • The returned response.
  • Timestamp.

To set up the model, create a directory named models on the project root directory. Inside the models directory, create a file named AuditTrail.js.

In the AuditTrail.js:

  • Start by importing the Schema, and model from mongoose:
   const {Schema,model} = require('mongoose');
Enter fullscreen mode Exit fullscreen mode
  • Define the audit trail schema as below:

    const auditTrail = new Schema({
       url:{
          type:Schema.Types.String,
          required:true
       },
       activity:{
          type:Schema.Types.String,
          required:true
       },
       params:{
          type:Schema.Types.String,
          required:true
       },
       query:{
          type:Schema.Types.String,
          required:true
       },
       payload:{
          type:Schema.Types.String,
          required:true
       },
       response:{
          type:Schema.Types.String,
          required:true
       }
    },{
       timestamps:true
    });
    
    • Export the model:
    module.exports = model('auditTrail',auditTrail);
    

With the model setup, the next step is to create a functionality to connect to the database. In the project root directory, create a directory named: utils. Inside the utils directory, create a file named: db.js.

In the db.js file:

  • Import the mongoose package:
  const mongoose = require("mongoose");
Enter fullscreen mode Exit fullscreen mode
  • Configure the path to the .env file:
  const {join} = require("path");
  require("dotenv").config({
    path:join(__dirname,'..','.env')
  });
Enter fullscreen mode Exit fullscreen mode
  • Define a function for connecting to the database:
  exports.connectDb = async () => {
    try{
        await mongoose.connect(process.env.DB_URL);
        console.log(">>>>>>>> Db is connected successfully >>>>>>>>>>");
    }catch(error){
        console.log(">>>>>>> an error occurred connecting to db >>>>>>>>>>>");
        console.log(error.message);
     }
  }
Enter fullscreen mode Exit fullscreen mode

Since we are accessing the DB_URL variable from the above function, we will need to edit the .env file and specify it:

DB_URL=mongodb://127.0.0.1:27017/auditTrails
PORT=4000
Enter fullscreen mode Exit fullscreen mode

To be able to interact with the model we just created, we need to import the connectDb function to the entry point file and ensure the function is called when application is been started.

Edit the app.js as follows:

  • Import the connectDb function:
  const {connectDb} = require("./utils/db");
Enter fullscreen mode Exit fullscreen mode
  • Call the function when the application is been started:
  app.listen(process.env.PORT,async () => {
    await connectDb();
    console.log("Server started and running on port "+process.env.PORT)
  });
Enter fullscreen mode Exit fullscreen mode

Now that we have the database connection set up, the next step is to set up a middleware that will be recording audit logs whenever any route is been hit.

Setting up the logger middleware

In the project root directory, create a directory named middlewares. In it, create a file named: auditTrail.js.

In the auditTrail.js:

  • Import the model:
  const AuditTrail = require('../models/AuditTrail');
Enter fullscreen mode Exit fullscreen mode
  • Define the methods in the routes and their actions:
  const methodMappers = {
    "GET":"Fetching",
    "POST":"Adding",
    "PUT":"Updating",
    "DELETE":"Deleting"
  }
Enter fullscreen mode Exit fullscreen mode
  • Define the middleware:
  exports.logAuditTrails = (req,res,next) => {
    try{
        const originalJson = res.json;
        res.json = async function (body){
            await AuditTrail.create({
                url:req.originalUrl,
                activity:methodMappers[req.method] + ' ' + req.originalUrl.split("/")[req.originalUrl.split("/").length - 1] || "",
                params:JSON.stringify(req.params),
                query:JSON.stringify(req.query),
                payload:JSON.stringify(req.body),
                response:JSON.stringify(body)
            });
            return originalJson.call(this,body);
        }
        next();
    }catch(error){
        console.log(">>>>> an error occurred logging audit trail >>>>>>>>");
        console.log(error.message);
        next();
    }
  }
Enter fullscreen mode Exit fullscreen mode

From above, we are basically embedding a function on the res.json function. In it, we are fetching the data from the request and response body to create the audit trail. After saving the record, we are calling the next() so as the original response can be shown to the user.

In the event that there is an error creating the audit trail, the error is logged and the response returned to the user. This way, the user will interact with the application as normal.

With the middleware set up, we need to make sure that it is applied on all the routes. To do so, we will edit the entry point file app.js as follows:

  • Import the middleware function:
  const {logAuditTrails} = require("./middlewares/auditTrail");
Enter fullscreen mode Exit fullscreen mode
  • Before defining the routes, add the following snippet :
  app.use(logAuditTrails);
Enter fullscreen mode Exit fullscreen mode

With that set up, we are ready to begin testing the application.

Testing the application

To start the Node.js server, we will need to add a script for that in the package.json file.

Edit your package.json by adding the below key value pair on the scripts section:

"start":"node app.js"
Enter fullscreen mode Exit fullscreen mode

Start the server by running the below command:

npm run start
Enter fullscreen mode Exit fullscreen mode

Once the server is started, open up Postman and send the following requests:

If all is successful and no error has been logged on the console, it means that the audit trails have been created on the database. Simply open up MongoDB Compass on mongodb://127.0.0.1:27017 and your data will be under the auditTrails database.

If an error occurred, kindly revisit the steps.

A take home task alongside this is to create an endpoint for fetching the audit trails from the database. This way, to retrieve your records you won't need to be using MongoDB Compass you will simply be sending a request via Postman.

For your reference, you can access the code from this Github Repository

Top comments (0)