Prisma client errors with NestJs
In this post, I am going to explain how to handle the Prisma client error.
Prisma clients have some error types, we split that error depending upon that type. If you want more information to refer click here
I have handled all of these Prisma client errors with the help of an instance. The reason for this means we can define which HTTP status code match that particular error. Because my application is implemented by REST API so must follow which standards provided by REST.
If you don’t aware of how to implement an exception filter refer to here and also I will cover that in this post. It’s nothing but we have captured all errors in our whole application with the help of this exception filter. If we define this class as the main file the NestJs handle it.
To create a global-scoped filter in the main.ts file, you would do the following:
import { AppModule } from './app.module';
import { useContainer } from 'class-validator';
import { ValidationPipe } from '@nestjs/common';
import { HttpExceptionFilter } from './exceptions/all-exception.filter';
import { NestFactory, HttpAdapterHost, Reflector } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
async function bootstrap() {
try {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
useContainer(app.select(AppModule), { fallbackOnErrors: true });
app.useGlobalPipes(
new ValidationPipe({ whitelist: true, transform: true }),
);
//exception-filter
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new HttpExceptionFilter(httpAdapter));
await app.listen(3000, '0.0.0.0');
} catch (error) {
logger.error({ err: error });
process.exit();
}
}
bootstrap();
While the base (built-in) exception filter can automatically handle many cases for you, you may want full control over the exceptions layer. Exception filters are designed for exactly this purpose. They let you control the exact flow of control and the content of the response sent back to the client.
Let’s create an exception filter that is responsible for catching exceptions that are an instance of the HttpException class, and implementing custom response logic for them. To do this, we'll need to access the underlying platform Request and Response objects. In my application, I have implemented it in Fastify. So I can get the error instance and message with the help of FastifyError. Easily I can split the error based on the Prisma client-type error.
import { FastifyError } from 'fastify';
import { AbstractHttpAdapter } from '@nestjs/core';
import {
Catch,
HttpException,
ArgumentsHost,
ExceptionFilter,
} from '@nestjs/common';
import {
PrismaClientRustPanicError,
PrismaClientValidationError,
PrismaClientKnownRequestError,
PrismaClientUnknownRequestError,
PrismaClientInitializationError,
} from '@prisma/client/runtime';
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
constructor(private readonly httpAdapterHost: AbstractHttpAdapter) {}
catch(exception: FastifyError, host: ArgumentsHost): void {
let errorMessage: unknown;
let httpStatus: number;
const httpAdapter = this.httpAdapterHost;
const ctx = host.switchToHttp();
if (exception instanceof PrismaClientRustPanicError) {
httpStatus = 400;
errorMessage = exception.message;
} else if (exception instanceof PrismaClientValidationError) {
httpStatus = 422;
errorMessage = exception.message;
} else if (exception instanceof PrismaClientKnownRequestError) {
httpStatus = 400;
errorMessage = exception.message;
} else if (exception instanceof PrismaClientUnknownRequestError) {
httpStatus = 400;
errorMessage = exception.message;
} else if (exception instanceof PrismaClientInitializationError) {
httpStatus = 400;
errorMessage = exception.message;
} else if (
exception.statusCode &&
exception.statusCode >= 400 &&
exception.statusCode <= 499
) {
httpStatus = exception.statusCode;
errorMessage = exception.message;
} else {
httpStatus = 500;
errorMessage = [
'Sorry! something went to wrong on our end, Please try again later',
];
}
const errorResponse = {
errors: typeof errorMessage === 'string' ? [errorMessage] : errorMessage,
};
httpAdapter.reply(ctx.getResponse(), errorResponse, httpStatus);
}
}
Catch all errors and return them from the controller. Already defined the exception filter as a global filter in the main.ts file, it will handle all errors through by HttpExceptionFilter class
import { FastifyReply } from 'fastify';
import { UsersService } from './users.service';
import {
Res,
Post,
Body,
HttpStatus,
Controller,
} from '@nestjs/common';
@Controller('v1/users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(
@Body() createUserDto: any,
@Res() reply: FastifyReply,
) {
return this.usersService
.create(createUserDto, )
.then((user) => {
reply.code(HttpStatus.CREATED).send(user);
})
.catch((error) => reply.send(error));//catch the error in your controller level
}
At the service level, we are handling the business logic and creating a new user record in the database by Prisma.
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaService } from 'src/configs/database';
const prisma = new PrismaClient();
@Injectable()
export class UsersService {
constructor() {}
async create(createUserDto: any,) {
const user = await prisma.user.create({
data: { ...createUserAttribites },
});
return user;
}
}
Conclusion
I hope, In this post, we have learned about how to handle the Prisma client error, Thank you for reading this post, See you soon.