conaticus
conaticus

Reputation: 461

Handle Prisma errors with Express

I am having some issues with error handling using ExpressJS and Prisma. Whenever a Prisma Exception occurs, my entire Node application crashes, and I have to restart it. I have done some googling and have looked at the Prisma Docs for error handling, but I can't find any answers.

I know I could possibly use try and catch, but this feels unnecessary, as I could handle this much better with an error handler, especially when a lot of information on errors is passed through Prisma.

I have tried to implement the Express error handler like this:

// index.ts

import errorHandler from "./middleware/errorHandler";
...
server.use(errorHandler);

// errorHandler.ts

import { NextFunction, Response } from "express";

// ts-ignore because next function is required for some weird reason
// @ts-ignore
const errorHandler = (err: any, _: any, res: Response, next: NextFunction) => {
    console.error(err.stack);
    res.status(500).send("Internal Server Error");
};

export default errorHandler;

This works fine for normal errors, but doesn't execute for Prisma errors, but instead just crashes the Node application.

How can I implement an error handler so I can manage Prisma Expections?

Upvotes: 9

Views: 9476

Answers (3)

Leandro
Leandro

Reputation: 5

your module:

 @Module({
  imports: [UsersModule, AuthModule, DealershipModule],
  controllers: [],
  providers: [
    PrismaService,
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}

your main add this line:

app.useGlobalFilters(new HttpExceptionFilter());

try this repo: https://www.npmjs.com/package/http-exception-filter-prisma-nestjs

Upvotes: 0

Aamir Khan
Aamir Khan

Reputation: 3031

As of now (Express < 5), Express invokes the error handler automatically only when an exception is encountered when executing synchronous code. For async code, you need to manually pass the error to next().

As noted in the Express error handling docs:

Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it.

For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them.

You absolutely can use your Express error handler with try and catch like so.

try {
  await prismaOperation();
} catch (e: unknown) {
  next(e);
}

If you pass anything to the next() function (except the string 'route'), Express regards the current request as being an error and will skip any remaining non-error handling routing and middleware functions.

If prismaOperation throws an error, the catch block will execute where you will have to manually pass the error to next(). Express will then skip all remaining middlewares and execute the error handler.

Starting with Express 5, this behaviour will be automated, as noted in the docs:

Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error.

Upvotes: 2

Manpreet Krishan
Manpreet Krishan

Reputation: 357

I ran into this problem today and I couldn't find answers too. I believe we'll have to write our own custom error handler for Prisma exceptions and not throw the error.

try {
  await prismaOperation();
} catch(e) {
  throw e; // avoid this which will crash our app
  /* Process Prisma error with error codes
     and prepare an appropriate error message
  */
  const error = prismaCustomErrorHandler(e);
  res.send(error); // Sending response instead of passing it to our default handler
}

Also,

// ts-ignore because next function is required for some weird reason

In Express, error-handling functions have 4 arguments instead of 3: (err, req, res, next).

Express interprets a middleware function with 3 arguments as (req, res, next) which is different from, had you ommited the 4th argument, (err, _, res). Hence, Express won't pass any error and your err will be a req object, _ (req) a res object and res a next function.

Edit:

...
const error = prismaCustomErrorHandler(e);
  res.send(error); // Sending response instead of passing it to our default handler
...

// Edit: Or you could process and pass the error using `next(error)` to default error handler.

The above method won't crash the app and indeed sends the response. But whether you use next or res.send, errors have to be processed.

Upvotes: 8

Related Questions