As for now, I’m using mongoose middleware to handle Mongoose specific errors (validation, cast, ….).
I’m using the following code in all of my schemas:
schema.post('save', handleValidationError);
schema.post('findOneAndUpdate', handleValidationError);
schema.post(['findOne', 'deleteOne'], handleCastError);
Is there anyway to make this global in all schemas without repeating the code?
I tried to use plugins as the following but they don’t get triggered if an error happens.
const errorsPlugin = (schema: any, _options: any): void => {
schema.post('save', handleValidationError);
schema.post('findOneAndUpdate', handleValidationError);
schema.post(['findOne', 'deleteOne'], handleCastError);
};
const connection = await mongoConnect(url);
plugin(errorsPlugin);
logger.info(`MongoDB connected: ${connection.connection.name}`);
Edit 1: error handler function
const handleValidationError = (error: NodeJS.ErrnoException, _res: Response, next: (err?: Error) => void): void => {
if (error.code?.toString() === '11000') {
const { keyPattern, keyValue } = error as Error & {
keyPattern: { [key: string]: number };
keyValue: { [key: string]: string };
};
const key = Object.keys(keyPattern)[0];
const value = Object.values(keyValue)[0];
throw new DuplicatedKeyError(key as string, value as string);
} else if (error instanceof mongooseError.ValidationError) {
throw new ValidationError(error.errors);
} else if (error instanceof mongooseError.CastError) {
throw new CastError(error.kind, error.value);
}
next();
};
4
Answers
You may consider this approach:
Create an ErrorResponse Class
Create an error handler middleware
Change your controllers
Use middleware after defining your routes
When any errors will be thrown from any where they will go to errorhandler(error.js) from their they will return to front-side and we can handle any kind of error using any custom messages or status codes with below method.
app.js
Middleware/error.js
helper/res_help.js;
your controllers //wrap your code in asyncHandler you want even need to use try-catch
Middleware/async.js
You can define the middleware functions in a separate file and then import them into your schemas.
Create a new file errorHandler.js and declare your middleware functions inside:
Then import file in node.js schemas by:
When you have multiple schemas in your application, defining the same middleware functions in every schema file can lead to code duplication and make your code harder to maintain. In such cases, it’s a good practice to define the middleware functions in a separate file and import them into your schema files.
This is an interesting insight and a nice approach you are taking, to solving the problem globally for all schemas.
Looking at the documentation, it looks like you are 90% there. The only thing I can spot that differs with the documentation is how you are applying your plugin.
To apply the plugin to a single schema you should be making the following call:
To apply the plugin to all schemas in the project, you can use the global mongoose object:
The above call should happen before you create any models, for example with mongoose.model("MyModel", mySchema). If you are creating all your models in a single file, it could look like this:
However if you are co-locating the model creation with the schema definition, for example:
After, you should make the call before requiring/exporting any models at all so that the plugin statement is evaluated before the model files are executed. For example:
I hope this helps 🙂