Title: Handling Errors Like a Pro: Sending Original Errors to Sentry While Keeping Client Responses Clean
I recently ran into a challenge with error handling in my NestJS project. It’s a common scenario: you want to simplify errors for the client but still capture the original, raw error for debugging and monitoring purposes in Sentry. The problem? My current approach was sending the simplified error to Sentry, not the actual, unprocessed error. Here’s how I solved it.
My Story: A Misstep in Error Handling
I was working on the create
function of my NestJS service, responsible for creating a new collection company. Everything was fine until I realized that while I was throwing clean, client-friendly error messages using HttpException
, I wasn’t logging the full, raw error in Sentry.
For example, my code looked something like this:
throw new HttpException(errorMessage, errorStatus);
Sure, my errorConvertor
function transformed complex errors into simplified errorMessage
and errorStatus
for the client, but Sentry was capturing these processed errors instead of the original error. When debugging, this was less than helpful.
I needed to send the raw error to Sentry while still keeping my responses clean for the clients. Here’s what I did.
The Solution: Separating Error Logging and Client Responses
I realized I could solve this by explicitly logging the raw error to Sentry before transforming it. Here’s how I updated my code:
Updated create
Function
async create(createCollectionCompanyDto: CreateCollectionCompanyDto) {
try {
const res = await this.collectionCompanyModel.create(createCollectionCompanyDto);
return res;
} catch (err) {
// Log the raw error to Sentry
Sentry.captureException(err);
// Simplify the error for the client
const { errorMessage, errorStatus } = errorConvertor(err);
// Throw the simplified error for the client
throw new HttpException(errorMessage, errorStatus);
}
}
Why This Works
- Capturing the Original Error:
By callingSentry.captureException(err)
directly inside thecatch
block, I ensure that the original error, with all its valuable debugging information (stack trace, error type, etc.), is logged to Sentry. - Client-Friendly Responses:
UsingerrorConvertor
, I process the raw error into a clean and understandable format for the client. This keeps the client-side experience polished and professional. - Separation of Concerns:
This approach separates error logging from client communication, ensuring that both tasks are handled independently and effectively.
Refining the Sentry Filter (Optional Improvement)
For projects where errors are also globally handled in a custom exception filter, you can further enhance the SentryFilter
to accommodate both raw and transformed errors. Here’s an example:
@Catch()
export class SentryFilter extends BaseExceptionFilter {
catch(exception: HttpException | Error, host: ArgumentsHost) {
// Log additional details to Sentry
if (exception instanceof HttpException) {
const response = exception.getResponse();
const status = exception.getStatus();
Sentry.captureException({
message: exception.message,
statusCode: status,
context: response,
});
} else {
Sentry.captureException(exception);
}
super.catch(exception, host);
}
}
This ensures that any error caught by the global filter — whether it’s raw or transformed — gets properly logged in Sentry with all relevant context.
My Takeaway
The key lesson here is that error handling is not a one-size-fits-all approach. While you want your users to see simplified, helpful error messages, your backend should log every detail to give you the tools to debug effectively.
How This Changed My Workflow
After implementing this, I immediately saw the benefits. Debugging became significantly easier because I had access to the raw error details in Sentry. At the same time, my clients continued to see clean, user-friendly error messages.
This approach has now become a best practice for me, and I hope it helps you as much as it helped me.
Final Thoughts
If you’ve ever struggled with balancing client-friendly error responses and detailed logging for debugging, this is the way to go. By separating logging and responses, you get the best of both worlds: happy clients and a happy dev team.
Happy coding! ๐