- First you need to create a custom Error class extending the Base Error class
- You have to make the class flexible to be able to take the error codes that you may wish to pass along
- Create the middleware to handle the errors.
As for mine I have setup one that handles some password validation and mongoose errors. Hoping you too can extend the functionalities here to match your needs
The custom error class extending base error
interface IErrorResponse {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string | symbol]: any
statusCode?: number
message: string
code?: number
}
class ErrorResponse extends Error implements IErrorResponse {
public statusCode: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any
constructor(message: string, statusCode: number) {
super(message)
this.statusCode = statusCode
}
}
export default ErrorResponse
The middleware
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Request, Response, NextFunction } from "express"
import capitalize from "../utils/capitalize"
import ErrorResponse from "./ErrorResponse"
export default function (
err: ErrorResponse,
req: Request,
res: Response,
next: NextFunction,
): Response<any, Record<string, any>> {
let error: ErrorResponse&{[key:string]:any} = { ...err }
error.message = err.message
// console.log(err)
if (err.code === 11000) {
const message = Object.keys(err).map((k) =>
capitalize(`${k} already exist`),
)
error = new ErrorResponse(String(message), 400)
}
if (err.name === "ValidationError") {
const message = Object.keys(err).map(function (value) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return typeof value === "object" ? error["message"] : value
})
console.log("Parsing ", message)
error = new ErrorResponse(String(message), 400)
}
if (error.message.split(/\n/).length > 1) {
return res.status(error.statusCode || 500).json({
success: false,
message:
error.message
.replace(/\t/, "")
.split("\n")
.filter((e) => e !== "") || "Internal server error",
})
}
return res.status(error.statusCode || 500).json({
success: false,
message: error.message || "Internal server error",
})
}
In this away possibly these are the errors you can catch
I have this password validator that does append some string on the error and terminates each error with a \n
so the error handler is able to split the errors and give me a list of the errors in JSON
format
passwordRegex = function ({
props,
fields,
}: {
props: { [x: string]: string },
fields: { fieldName: string, name: string }[],
}): { passOK: boolean, errors: string } {
let errors = ""
try {
for (let key of Object.keys(props)) {
if (fields.some((f) => f.fieldName === key)) {
const regex = new RegExp(props[key], "i")
const field = fields.find((f) => f.fieldName === key)
if (regex.test(props!["password"])) {
errors += `Password should not contain your ${field!.name}\n`
}
}
}
if (!/[a-z]/.test(props["password"])) {
errors += "Password must contain at least 1 lowercase letter\n"
}
if (!/[A-Z]/.test(props["password"])) {
errors += "Password must contain at least 1 uppercase letter\n"
}
if (!/[0-9]/.test(props["password"])) {
errors += "Password must contain at least a number\n"
}
if (!/[\w]{7,16}/.test(props["password"])) {
errors += "Password must be at least 8 characters long\n"
}
if (/[.*+?^${}#%^@!`()|[\]\\]{4,}/.test(props["password"])) {
errors +=
"Password must not contain more than 4 repeating characters\n"
}
if (!/[.*+?^${}#%^@!`()|[\]\\]/.test(props["password"])) {
errors +=
"Password must be at least 1 special character (.*+?^${}#%^@!`())\n"
}
if (errors !== "") {
return { passOK: false, errors }
}
return { passOK: true, errors }
} catch (err) {
return { passOK: false, errors }
}
}
How do I pass this as a middleware?
export default ({ app }:{app:Application}) => {
app.use(ExpressError)
}
Passing error state
const { passOK, errors } = passwordUtils.passwordRegex({
props: req.body,
fields: [
{ fieldName: "firstName", name: "First name" },
{ fieldName: "lastName", name: "Last name" },
{ fieldName: "email", name: "Email" },
],
})
if (!passOK) {
return next(new ErrorResponse(errors, 400))
}
Hope this helps answer your question to customize your errors
I have just given an overview but you can proceed to checking the exact codes and values of the errors to determine the type of error and return the appropriate error message or log somewhere