Overview

Convert Prisma error codes to user-friendly HTTP responses.

Features

  • Unique constraint errors
  • Not found errors
  • Foreign key errors
  • Validation errors

Installation

npx hanma add prisma-exception-filter --framework nest

Dependencies

@prisma/client

Code

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpStatus,
  Logger,
  ConflictException,
  NotFoundException,
  BadRequestException,
  InternalServerErrorException,
} from "@nestjs/common";
import { Prisma } from "@prisma/client";
import { Request, Response } from "express";

export interface PrismaErrorResponse {
  statusCode: number;
  error: string;
  message: string;
  code?: string;
  meta?: Record<string, any>;
  path: string;
  timestamp: string;
}

@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(PrismaExceptionFilter.name);

  catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const { status, error, message } = this.mapPrismaError(exception);

    this.logger.warn(
      `Prisma error ${exception.code}: ${exception.message}`,
      {
        code: exception.code,
        meta: exception.meta,
        path: request.url,
      }
    );

    const errorResponse: PrismaErrorResponse = {
      statusCode: status,
      error,
      message,
      code: exception.code,
      meta: process.env.NODE_ENV === "development" ? exception.meta : undefined,
      path: request.url,
      timestamp: new Date().toISOString(),
    };

    response.status(status).json(errorResponse);
  }

  private mapPrismaError(exception: Prisma.PrismaClientKnownRequestError): {
    status: number;
    error: string;
    message: string;
  } {
    switch (exception.code) {
      // Unique constraint violation
      case "P2002": {
        const fields = (exception.meta?.target as string[]) || ["field"];
        return {
          status: HttpStatus.CONFLICT,
          error: "Conflict",
          message: `A record with this ${fields.join(", ")} already exists`,
        };
      }

      // Foreign key constraint violation
      case "P2003": {
        const field = exception.meta?.field_name as string;
        return {
          status: HttpStatus.BAD_REQUEST,
          error: "Bad Request",
          message: `Invalid reference: ${field || "related record"} does not exist`,
        };
      }

      // Record not found
      case "P2001":
      case "P2018":
      case "P2025": {
        return {
          status: HttpStatus.NOT_FOUND,
          error: "Not Found",
          message: "The requested record was not found",
        };
      }

      // Value too long for column
      case "P2000": {
        const column = exception.meta?.column_name as string;
        return {
          status: HttpStatus.BAD_REQUEST,
          error: "Bad Request",
          message: `Value too long for ${column || "field"}`,
        };
      }

      // Required field missing
      case "P2011":
      case "P2012": {
        return {
          status: HttpStatus.BAD_REQUEST,
          error: "Bad Request",
          message: "A required field is missing",
        };
      }

      // Invalid ID
      case "P2023": {
        return {
          status: HttpStatus.BAD_REQUEST,
          error: "Bad Request",
          message: "Invalid ID format",
        };
      }

      // Query interpretation error
      case "P2016":
      case "P2017": {
        return {
          status: HttpStatus.BAD_REQUEST,
          error: "Bad Request",
          message: "Invalid query parameters",
        };
      }

      // Default
      default:
        return {
          status: HttpStatus.INTERNAL_SERVER_ERROR,
          error: "Internal Server Error",
          message: "A database error occurred",
        };
    }
  }
}

// Catch Prisma validation errors
@Catch(Prisma.PrismaClientValidationError)
export class PrismaValidationFilter implements ExceptionFilter {
  private readonly logger = new Logger(PrismaValidationFilter.name);

  catch(exception: Prisma.PrismaClientValidationError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    this.logger.error("Prisma validation error", exception.message);

    response.status(HttpStatus.BAD_REQUEST).json({
      statusCode: HttpStatus.BAD_REQUEST,
      error: "Bad Request",
      message: "Invalid data provided",
      path: request.url,
      timestamp: new Date().toISOString(),
    });
  }
}

// Usage in AppModule:
/*
import { APP_FILTER } from "@nestjs/core";
import { PrismaExceptionFilter, PrismaValidationFilter } from "./filters/prisma-exception.filter";

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: PrismaExceptionFilter,
    },
    {
      provide: APP_FILTER,
      useClass: PrismaValidationFilter,
    },
  ],
})
export class AppModule {}
*/

Usage

// Add to global filters
app.useGlobalFilters(new PrismaExceptionFilter());

Dependencies

Frequently Asked Questions

How do I install Prisma Exception Filter?
Run the following command: npx hanma add prisma-exception-filter --framework nest
What dependencies are required?
This snippet requires: @prisma/client. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to src/filters/prisma-exception.filter.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.