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:
- Basic knowledge working with Node.js.
- Basic knowledge working with Express.js.
- Postman installed on your computer.
- MongoDB and MongoDB Compass installed on your computer.
Overview:
- Setting up the application.
- Setting up the models.
- Setting up the logger middleware.
- Testing the application
Setting up the application:
- Proceed to your preferred working directory.
- From the terminal, initialize an NPM project using the default configurations:
npm init -y
-
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
- 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 forPOST
requests:
app.use(express.json());
- Set up dummy
GET
,POST
, andPUT
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
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');
-
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");
- Configure the path to the .env file:
const {join} = require("path");
require("dotenv").config({
path:join(__dirname,'..','.env')
});
- 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);
}
}
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
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");
- 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)
});
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');
- Define the methods in the routes and their actions:
const methodMappers = {
"GET":"Fetching",
"POST":"Adding",
"PUT":"Updating",
"DELETE":"Deleting"
}
- 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();
}
}
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");
- Before defining the routes, add the following snippet :
app.use(logAuditTrails);
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"
Start the server by running the below command:
npm run start
Once the server is started, open up Postman and send the following requests:
- A GET request to http://localhost:4000/.
- A POST request to http://localhost:4000/. Make sure you include some JSON payload on it.
- A PUT request to http://localhost:4000/kitty: Make sure you include some JSON payload too.
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)