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,
}
Related
I have a Pub/Sub triggered cloud function that calls an API end-point and logs the message. But I am not seeing all log messages being logged in console except everything right before calling API.
Once the API is called I am logging the response, and exception messages in case of any error.
It is logging: Function execution took 120015 ms. Finished with status: timeout Earlier the default timeout was set to 60 sec, later I increased it to 120 sec. Still the problem persist.
I am not understanding the issue here since it is working locally without any issues.
Here I have custom module to log messages to Winston and GCP console (it doesn't have any issue and working fine).
Code calling the API module:
const console = require('./logging-utils');
const portal_api = require('./api-utils');
exports.triggerPortalNotifier = async (event, context) => {
try {
/*
.....
*/
console.metadata.cloudFunction = cf_name;
console.metadata.requestId = requestId;
console.metadata.organizationId = organizationId;
console.metadata.instanceId = instanceId;
console.logMessage(`Event received with payload: some message`);
var payload = {
//payload to API
}
var response = await portal_api.notifyPortal(payload);
console.logMessage(`Response received from portal API is: ${JSON.stringify(response.data)}`);
}
else {
throw new Error(`Invalid message received: ${_message}`);
}
}
catch (error) {
console.logMessage(`Portal API failed with exception: ${error}`);
throw new Error(`${error.message}`);
}
}
Code that make API request (using axios module)
require('dotenv').config();
const axios = require('./axios-instance');
const console = require('./logging-utils');
const nextgen_api = {
notifyPortal: async (payload) => {
try {
const config = {
headers: {
'Authorization': process.env.PORTAL_AUTHORIZATION_TOKEN,
'Content-Type': "application/json",
'Accept': "application/plain"
}
}
console.logMessage(`Input paylod for API end-point: ${process.env.PORTAL_API} => ${JSON.stringify(payload)}`)
const response = await axios.post(process.env.PORTAL_API, JSON.parse(JSON.stringify(payload)), config);
console.logMessage(`Response from API: ${JSON.stringify(response.data)}`);
return response;
}
catch (err) {
if (err.response && err.response.status !== 200) {
console.logMessage(`API call failed with status code: ${err.response.status} `);
throw new Error(`API call failed with status code: ${err.response.status} `);
}
else {
console.logMessage(`API call failed with ${err.stack}`);
throw new Error(`API call failed with status code: ${err.stack} `);
}
}
}
}
module.exports = my_api;
Message Response from API: ${JSON.stringify(response.data)} is not being logged.
Any help here is appreciated.
I am trying to run a test that says
test("POST /register, malformed and edge cases", async () => {
await request(server).post(`/auth/register`).expect(400);
});
I am sending an empty request body. My goal is to trigger the error handler to respond with some sort of 4xx status code.
I am working using the boilerplate in this blog: https://jasonwatmore.com/post/2020/05/13/node-mongo-api-with-email-sign-up-verification-authentication-forgot-password
Here's what I'm doing:
I hit this route with a POST request: this.router.post("/register", registerUserSchema, this.register);
I expect that the registerUserSchema is engaged and indeed it is. I can tell because a console.log statement happens. Then
function registerUserSchema(req: Request, res: Response, next: NextFunction) {
const schema: ObjectSchema<any> = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
confirmPassword: Joi.string().valid(Joi.ref("password")).required(),
acceptTerms: Joi.boolean().valid(true).required(),
});
validateRequest(req, next, schema);
}
the function downstream of registerUserSchema is this: validateRequest(req, next, schema); which does occur as I expedct, leading to
this
function validateRequest(req: Request, next: NextFunction, schema: ObjectSchema<any>) {
const options = {
abortEarly: false, // include all errors
allowUnknown: true, // ignore unknown props
stripUnknown: true, // remove unknown props
};
const { error, value } = schema.validate(req.body, options);
console.log(error, "12rm");
if (error) {
next(`Validation error: ${error.details.map(x => x.message.replaceAll('"', "")).join(", ")}`);
// next(error);
} else {
req.body = value;
next();
}
}
export default validateRequest;
I know that this "Validation error:" text makes it out of the server because it shows up in Postman.
However, what I really want is to be able to modify the status code from 500 to 4xx. I presumed this errorHandler would do it because I say this.app.use(errorHandler); in my app.ts file. But it doesn't happen, my console.log doesn't do anything during a malformed request to /auth/register
function errorHandler(err: any, request: Request, response: Response) {
console.log("In the error handler, 5rm", err, typeof err);
}
If someone can enlighten me: How do I anticipate where next() will activate next in my application?
In this case the solution was
const err = new Error(`Validation error: ${error.details.map(x => x.message.replaceAll('"', "")).join(", ")}`);
err.name = "ValidationError";
next(err);
then the error handler figured out it was next as I expected it to
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.
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
}])
I created a interceptor for my angular project to intercept all the requests and responses, but the function that validates errors in the responses is executed 7 times.
I realized that when I use the throwError of rjxs it performs the function many times, if I use the of rxjs it executes only one, but fails to execute functions that validate errors in subscribes.
constructor(private injector: Injector, public errorHandler: ApplicationErrorHandler) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const sessaoService = this.injector.get(SessaoService);
if (sessaoService.isLoogedIn()) {
const token = localStorage.getItem('token');
const tokenSplit = token.split(' ');
request = request.clone(
{ setHeaders: { 'Authorization': `${tokenSplit[1]}` } }
);
}
return next.handle(request).pipe(
catchError((err: HttpErrorResponse) => {
console.log('Execute function');
let data = {};
data = {
error: err,
status: err.status,
method: request.method
};
this.errorHandler.handleError(data);
return throwError(err);
})
);
}
I expect that catchError function execute only one time, but it is running 7 times for each request.
My Angular version is: 6.1.3;
My Rxjs version is: 6.4.0;
Sorry for my bad english...
I found the problem, I have a shared module in my application and inside it I had added HTTP_INTERCEPTORS as provider using my class with theintercept function.