API with NestJS #130. Avoiding storing sensitive information in API logs

NestJS

This entry is part 130 of 180 in the API with NestJS

In previous parts of this series, we’ve learned how to implement logging in our REST API. Logs play an important role in debugging and maintaining reliable services. However, they can pose security and privacy risks when misused. In this article, we will learn how to prevent sensitive information from being stored in our logs.

Logging with an interceptor

With interceptors, we can access and manipulate the data flowing between the client and the server. We can apply interceptors to controllers and routes in a similar way as middleware. Since interceptors can access the request and response, we can use an interceptor that logs incoming requests and our responses.

logger.interceptor.ts

Our interceptors need to implement the interface. This requires us to implement the method that NestJS calls before invoking the controller method.

This means that the method is called before the method.

logger.interceptor.ts

The method has two arguments. The first is the that provides the information about the incoming request. By using the method, we can access the HTTP-specific context of a request. This allows us to use the and methods to access the and objects containing useful data related to the request and the response.

NestJS handles requests in a sequence called the request lifecycle that includes request processing, route handling, and generating the response. A request lifecycle consists of pieces such as guards, interceptors, pipes, and controller method handlers. The request lifecycle executes in a specific order. The second argument of the method is the object. Calling the method tells NestJS to go to the next part of the request lifecycle. Forgetting about calling it would cause our API to hang.

NestJS uses interceptors before sending the response. Since we want to include the information about the response in our logs, we need to wait for NestJS to prepare the response. To do that, we can wait for the event to happen.

logger.interceptor.ts

Above, we log a message that contains the HTTP method, the URL of the request, and the status. We can use the to attach our interceptor to all routes.

main.ts

If you want to know more about logging with NestJS, check out API with NestJS #113. Logging with Prisma

Handling the sensitive information

The above log might not be enough to debug our application and determine what’s wrong. To do that, we might want to store the request body.

The problem with the above approach is that we log sensitive information, such as plain text passwords. Let’s use the library to create a Data Transfer Object class that hides the password.

registrationRequestLogging.dto.ts

Above, we are using the decorator to store the type of the value instead of the value itself.

Attaching the DTO to the route

We now need a way to attach our new DTO class to a specific route. To do that, we can create a decorator that uses .

parseRequestBodyWhenLogging.ts

We are using the type from the type-fest library to represent any class.

With , we can assign metadata to a particular method.

authentication.controller.ts

Thanks to our decorator, we’ve assigned the class to the method. Let’s now modify our logging interceptor to take it into account. First, we need to give it access to the reflector that allows us to retrieve the metadata we assigned to the method.

main.ts

We can now create the method that uses the reflector to get the DTO class and create its instance.

logger.interceptor.ts

If a particular route does not have the decorator, our interceptor does not parse the body. If it does, we use the function to create an instance of our DTO class. Thanks to that, we can hide the actual value of the field.

In the above log, we can see that the endpoint responded with 400 Bad Request. The request body shows that the user provided a number as a password instead of a string. Therefore, our API is expected to respond with the error.

Summary

In this article, we’ve explained how to implement logging using interceptors. We’ve also created a custom decorator that allows us to avoid storing sensitive data in our logs. Feel free to use this solution in more routes, such as the one used for logging in. We can also use a similar approach to parsing the response body by creating a similar decorator. All of the above gives us much more control over what we store in our logs.

Series Navigation<< API with NestJS #129. Implementing soft deletes with SQL and KyselyAPI with NestJS #131. Unit tests with PostgreSQL and Kysely >>
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Tim
Tim
4 months ago

Thanks for your great series,
How can we use this interceptor (with constructer’s param) in controller scope?