Nestjs catch 500 error and return the error - nestjs

When an unhandled error occurs, the front side gets a 500 error without any information about the error. It just receives this:
{
"statusCode": 500,
"message": "Internal server error"
}
But when I check the console, I see what happened and what the error message is. It's ok in the development environment but it's hard to figure out in production. How can I return a complete error message in production instead of just a simple message like "Internal server error"?

Generally you should be doing error handling around things that have a chance to go wrong (database operations, sending emails, validations), but if you're looking for a general error handling mechanism, you can use the Exception Filters construct that Nest provides to catch errors and manage them as necessary.

NestJS has the NotFoundException you can use.
You can implement your own custom Exception filters and add it to your controllers to capture a NotFoundException or whatever communication protocol, but this is assuming that you have an HTTP communication protocol.
So if http is the only communication protocol you are dealing with then you can just write this into one of your methods inside your service:
if (!user) {
throw new NotFoundException('user not found');
}
And you would import that from:
import { Injectable, NotFoundException } from '#nestjs/common';

Finally found the answer to this question - you don't need any complicated exception filters, or interceptors, etc. You just need to wrap the code which is causing the 500 in an if statement, and throw a HttpException - note, throwing an Error will not work.
In other words, you need to resolve this error in your service class, not in your controller.
For example, in your service class:
async getUsers(){
let response = doSomething();
if (response.status !== 200) {
throw new HttpException(
`Error getting users: ${response.status} ${response.statusText}`,
response.status,
)
} else {
return response.data
}
}

//InternalServerError.filter.ts
import {
InternalServerErrorException,
Catch,
ArgumentsHost,
ExceptionFilter,
HttpStatus,
HttpException
} from "#nestjs/common";
// catch all error
#Catch()
export default class InternalServerErrorExceptionFilter implements ExceptionFilter {
catch(exception: Error, host: ArgumentsHost) {
let { message: errMsg, stack: errStack, name: errName } = exception;
// let errRes = exception.getResponse();
// let errCode = exception.getStatus();
let ctx = host.switchToHttp();
let req = ctx.getRequest();
let res = ctx.getResponse();
const statusName = "系统内部错误";
res.statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
// HttpException Error
if (exception instanceof HttpException) {
// set httpException res to res
res.status(exception.getStatus()).json(exception.getResponse());
return;
}
// other error to rewirte InternalServerErrorException response
res.render("Error.ejs", {
exception,
errMsg,
errStack,
errName,
statusCode: res.statusCode,
statusName,
req
});
}
}
then use global filter provider for InternalServerError.filter.ts
// app.module.ts
#Module({
providers:[{
provide: APP_FILTER,
useClass: InternalServerErrorExceptionFilter
}])

Related

NestJS - Prisma Exception Filter in Cron

In one of my nestjs projects I have created the prisma custom exception filters.
This code works properly for http request. But while I use cron then this does not work for me. In cron if any prisma error occurs then it stops execution and throw PrismaClientKnownRequestError error in the console directly.
Did I do any mistake? Or exception filter does not work in cron? Please help me! Any help will be appreciated. Thanks.
Prisma exception filter code:
#Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaClientKnownRequestErrorExceptionFilter extends BaseExceptionFilter {
logger = new Logger('PrismaClient');
catch(
exception: Prisma.PrismaClientKnownRequestError,
host: ArgumentsHost,
): void {
const ctx: HttpArgumentsHost = host.switchToHttp();
const request: Request = ctx.getRequest<Request>();
const response: Response = ctx.getResponse<Response>();
let statusCode: HttpStatus;
let message: string;
switch (exception.code) {
case 'P2002': // unique constraint or duplication
statusCode = HttpStatus.CONFLICT;
message = `Unique constraint failed on the constraint: ${exception?.meta?.['target']}`;
break;
....
default:
// super.catch(exception, host);
statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
message = exception.message.replace(/\r/g, '').replace(/\n/g, '');
break;
}
this.logger.error(message);
this.response(statusCode, message, request, response);
}
response(
statusCode: HttpStatus,
message: string,
request: Request,
response: Response,
) {
response.status(statusCode).json({
statusCode,
message,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
I added the filter in app module as provider like -
{
provide: APP_FILTER,
useClass: PrismaClientKnownRequestErrorExceptionFilter,
}

Proper way of formatting logs to sentry

I am using NestJS to build the rest APIs and trying to implement logging using Sentry but did not get the right way of formatting logs before sending them to sentry.
I already have a filter that catches every kind of log (HTTP as well as application errors). And I'm trying to implement sentry globally without having it to inject into each controller and service.
error-filter.ts
import {
HttpExceptionResponse,
CustomeHttpExceptionResponse,
} from './modals/http-exception-response.interface';
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '#nestjs/common';
import * as fs from 'fs';
import { Request } from 'express';
import { captureException } from '#sentry/node';
#Catch()
export class HttpErrorFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
let status: HttpStatus;
let errorMessage: string;
if (exception instanceof HttpException) {
status = exception.getStatus();
const errorResponse = exception.getResponse();
errorMessage =
(errorResponse as HttpExceptionResponse).error ||
exception.message ||
"Sorry, can't process this request at the moment!";
} else {
status = HttpStatus.INTERNAL_SERVER_ERROR;
errorMessage = 'Internal server error!';
}
const errorResponse = this.getErrorResponse(status, errorMessage, request);
const errorLog = this.getErrorLog(errorResponse, request, exception);
// this.writeErrorLogToFile(errorLog);
this.sendLogsToSentry(errorLog);
response.status(status).json(errorResponse);
}
private getErrorResponse = (
status: HttpStatus,
errorMessage: string,
request: Request,
): CustomeHttpExceptionResponse => ({
statusCode: status,
error: errorMessage,
path: request.url,
method: request.method,
timeStamp: new Date(),
message: errorMessage,
});
private getErrorLog = (
errorResponse: CustomeHttpExceptionResponse,
request: Request,
exception: unknown,
): string => {
const { statusCode, error } = errorResponse;
const { method, url } = request;
const errorLog = ` Response code: ${statusCode} - Method: ${method} - URL: ${url}\n
${JSON.stringify(errorResponse)}\n
User: ${JSON.stringify(request.user ?? 'User not signed in')}\n
Details: ${
exception instanceof HttpException ? exception.stack : error
}\n\n`;
return errorLog;
};
private writeErrorLogToFile = (errorLog: string): void => {
fs.appendFile('error.log', errorLog, 'utf8', (err) => {
if (err) throw err;
});
};
private sendLogsToSentry = async (errorLog: any) => {
captureException(errorLog);
};
}
main.ts
....
// Sentry setup for logging
const configService = app.get(ConfigService);
const sentryClientKey = configService.get('SENTRY_CLIENT_KEY');
Sentry.init({
dsn: sentryClientKey,
tracesSampleRate: 1.0,
});
These are the only two files I tried for setting up the sentry.
.....
With this, the errors are being successfully captured but not in the way they have to.
I can't get the full information like Stacktrace for non-HTTP errors, no user info, and many other missing pieces. I know that I've constructed the log as a string and sent it to Sentry but I tried sending the whole exception object too, and that event didn't work.
I tried looking into the documentation but couldn't figure it out. So how the logs should be formatted to get the most out of it in sentry? I am searching for a solution with less code change. But if it isn't possible without injecting Sentry into the service and controller, will do that. Please guide me on how.

How to return custom status codes in NestJs while using FileInterceptor to upload a file?

I am trying to return custom status codes for different type of exceptions. Although I'm receiving response properly, I'm not able to do it without causing an error. The error occurs only inside if condition block (if I send a file in post request). There is no error in else block.
Error: cyclic dependency detected
// Below code gives this error => Error: cyclic dependency detected
import { Controller, Post, Req, Res, UseInterceptors, UploadedFile } from '#nestjs/common';
import { FileInterceptor } from '#nestjs/platform-express';
import { Request, Response } from 'express';
#Controller('testing')
export class TestController {
constructor() { }
#Post('/upload')
#UseInterceptors(FileInterceptor('file'))
upload(#UploadedFile() file, #Res() response: Response) {
if (file && file !== undefined) {
return response.status(200).json({
status: "OK",
message: "File Uploaded"
});
} else {
return response.status(400).json({
status: "BAD REQUEST",
message: "File not found"
});
}
}
}
I got a similar error a while ago.
If you really want to use the #Res, Try to use it with the passthrough argument to maintain compatibility with Nest standard response handling.
Something like this (I did some refactoring to make it a bit more clean)
#Post("/upload")
#UseInterceptors(FileInterceptor("file"))
upload(#UploadedFile() file, #Res({ passthrough: true }) res: Response) {
if (file) {
res.status(HttpStatus.OK).json({
status: "OK",
message: "File uploaded",
});
} else {
res.status(HttpStatus.BAD_REQUEST).json({
status: "BAD REQUEST",
message: "File not found",
});
}
}
p.s.: And try to use HttpStatus enum to keep your code more readble
But there is a much better and clean solution. You just need to throw a BadRequestException with the message you want if the file is not there, and NestJS will magically take care of everything for you =D
#Post("/upload")
#HttpCode(HttpStatus.OK)
#UseInterceptors(FileInterceptor("file"))
upload(#UploadedFile() file) {
if (!file) {
throw new BadRequestException("File not found!");
}
// do something with the file...
}

Angular General Error Handling for unexpected errors

Is there a way to set a general error handling behaviour (e.g. to throw a general error message) in case there is anything going wrong with the server? In my project, I send or request data to or from the backend,
this.userService.post(this.user).subscribe( (reponse: Response) => {
if(response.status === 200){
// success
}
}, error => this.handleError(error._body));
and when there is anything wrong with the data or the user etc., I return a response which includes a individual error message in the body. But when there is an error which isn't thrown intentionally by the backend (e.g. an internal server error 500) I pass the stacktrace up to the user.
It would be better so set a general error message which is displayed in frontend when there is anything really going wrong in the backend. How? I'm looking for a solution where I don't have to distinguish in every backend request or post if the error is unexpected or thrown by the backend (if that helps: every error thrown by the backend intentionally have status code 409).
You can use interceptors:
generate a service and implement HttpInterceptor interface
#Injectable()
export class HttpErrorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error: any) => {
if (error instanceof HttpErrorResponse) {
if (error.status === 409) {
console.log('409 ERROR'); <-- gets called on each response
} else if(error.status === 500) {
console.log('500 ERROR'); <-- gets called on each response
}
}
return throwError(error);
}),
);
}
}
and then add HttpErrorService to your app.module.ts
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorService,
multi: true
},
]

NodeJs http status exception handling

I have created nodejs + express application. Now in my application when exception caught errors are send as follows
app.get('/data', (req, res) => {
if(!req.params.token){
return res.status(403).send('Access token not provided');
}
//do something here
});
Instead of sending res.status(403).send('Access token not provided'); can I send something like this
exception.js
class Forbidden {
constructor(message,stack = null){
this.code = 403;
this.message = message
this.stack = stack;
}
}
app.js
var httpForbidden = require('exception.js');
app.get('/data', (req, res) => {
if(!req.params.token){
return new httpForbidden ('Access token not provided');
}
//do something here
});
And also how can I caught all exceptions in once place ?
You could use something like this:
class httpError {}
class httpForbidden extends httpError {
constructor(message, stack = null) {
super();
this.code = 403;
this.message = message
this.stack = stack;
}
}
app.get('/', (req, res) => {
if (!req.params.token) {
throw new httpForbidden('Access token not provided');
}
...
});
app.use((err, req, res, next) => {
if (err instanceof httpError) {
return res.status(err.code).send(err.message);
}
res.sendStatus(500);
});
This uses an Express error handling middleware that will check if the error that got thrown is an instance of httpError (which would be the superclass of all the HTTP error classes that you'd want to create) and, if so, would generate a particular response according to the code and the message (or generate a generic 500 error response otherwise).
I like to create a separate function, along with other utility functions ( say in lib.js), which creates a properly formatted JSON response object and selects the appropriate logger to log response depending upon the HTTP status code.
lib.js
var logger = require("./loggger");
module.exports.sendResponse = function (res,code,message,data) {
if(code<100 || code>599) {
throw new Error("response cannot be sent. Invalid http-code was provided.");
}
var responseLogger = code>=500 ? logger.error : logger.debug;
var responseObject = {
"code" : code,
"message" : message
};
if(data) {
responseObject.data = data;
}
responseLogger(responseObject);
res.status(code).json(responseObject);
};
app.js
var lib = require("./lib");
/*
Relevant Express server code
*/
app.get('/data', function (req,res) {
if(!req.params.token){
return lib.sendResponse(res,403,"Access token not provided");
}
// Rest of business logic
});
Note : You can write your own logging functionality, but I strongly suggest to build it upon some standard logging library like winston)
Below method is deprecated as the boom is changes to #hapi/boom,
https://hapi.dev/family/boom/?v=8.0.1
here you find whole documentation of #hapi/boom library
-----deprecated-------
You can use boom library instead, which provides a set of utilities for returning HTTP errors
HTTP 4xx Errors
Boom.badRequest([message], [data])
Boom.unauthorized([message],[scheme], [attributes])
HTTP 5xx Errors
Boom.badImplementation([message], [data]) - (alias: internal)
Boom.notImplemented([message], [data])
for more api documentation visit here
You can use:
res.code(403).json({message: '...', stack: '...'});
and send whatever you want. But you do it with calling methods on the response object.
And also how can I caught all exceptions in once place ?
Very bad idea. You should handle all errors where they happen so that you can still have some context to handle them in a reasonable way. Otherwise you can just throw exceptions and return 500 errors.

Resources