I want to handle exceptions properly in my NestJS app. I have watched videos, read blogs and found some answers on stackoverflow but it is too confusing for me to implement proper exception handling.
The login method in user microservice(controller):
@GrpcMethod('UserService', 'Login')
async login(@Payload() payload: Login): Promise<any> {
try {
const { email, password } = payload;
const { user, token, refreshToken } = await this.usersService.login(email, password);
return {
user: {
email: user.email,
userId: user.id,
name: user.name,
phone: user.phone,
},
token,
refreshToken,
};
} catch (error) {
throw new RpcException(error);
}
}
The login method in user microservice(service) along with find user by email:
async findByEmail(email: string): Promise<any> {
const user = this.userModel.findOne({ email }).exec();
if (!user) return 'User not found!!';
return user;
}
async login(email: string, password: string): Promise<any> {
try {
const user = (await this.findByEmail(email)) as User;
const comparePassword = await passwordService.comparePasswords(password, user.password);
if (user && comparePassword) {
const { token, refreshToken } = await this.sessionService.createSession(user._id, {
userId: user._id,
type: user.type,
});
return { user, token, refreshToken };
}
} catch (error) {
throw new Error(error);
}
}
The controller method in API gateway file:
@Post('login')
async login(@Body() loginData: Login): Promise<any> {
try {
const user = await firstValueFrom(this.userService.Login(loginData));
return sendSuccess(user, 'Log-in successful.');
} catch (error) {
return sendError(error.details, 404);
}
}
I want to handle all the exceptions and throw appropriate exception. like if email is not registered then throw "Email not found" if password is wrong then throw "invalid credentials" and so on. How can I do that?
the request-response utility code:
export function sendSuccess<T>(data: T, message: string = 'Success', statusCode: number = HttpStatus.OK): ApiResponse<T> {
return {
status: 'success',
statusCode,
message,
data,
};
}
export function sendError<T>(message: string, statusCode: number): ApiResponse<T> {
return {
status: 'error',
statusCode,
message,
data: null,
};
}
export interface ApiResponse<T> {
status: 'success' | 'error';
statusCode: number;
message: string;
data: T | null;
}
@Catch()
export class AllExceptionsFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException ? exception.message : 'Internal Server Error';
response.status(status).json(sendError(message, status));
}
}
I have registered AllExceptionsFilter in my main.ts file: app.useGlobalFilters(new AllExceptionsFilter());
but it never gets there.
From what I can find in docs AllExceptionsFilter
should implements RpcExceptionFilter
and uses RpcException
for the @Catch
decorator.
import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { RpcException } from '@nestjs/microservices';
@Catch(RpcException)
export class ExceptionFilter implements RpcExceptionFilter<RpcException> {
catch(
exception: any, host: ArgumentsHost
): Observable<any> {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException ? exception.message : 'Internal Server Error';
return throwError(() => response.status(status).json(sendError(message, status)););
}
}
note that
The
catch()
method must return an Observable
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With