Repository: RobinTail/express-zod-api
Pull request #737 (2022-12-11 02:08:25)

Refactoring custom errors
Improving custom errors by giving them their well deserved names and liberating 'em from redundant methods :)


Patch:
diff --git a/src/errors.ts b/src/errors.ts
index 7857c1ed..1c36ef26 100644
--- a/src/errors.ts
+++ b/src/errors.ts
@@ -1,26 +1,29 @@
-export class RoutingError extends Error {}
+export class RoutingError extends Error {
+  public override name = "RoutingError";
+}
 
-export class DependsOnMethodError extends RoutingError {}
+export class DependsOnMethodError extends RoutingError {
+  public override name = "DependsOnMethodError";
+}
 
-export class OpenAPIError extends Error {}
+export class OpenAPIError extends Error {
+  public override name = "OpenAPIError";
+}
 
-export class IOSchemaError extends Error {}
+export class IOSchemaError extends Error {
+  public override name = "IOSchemaError";
+}
 
-export class OutputValidationError extends IOSchemaError {}
+export class OutputValidationError extends IOSchemaError {
+  public override name = "OutputValidationError";
+}
 
 export class ResultHandlerError extends Error {
-  protected readonly originalError?: Error;
+  public override name = "ResultHandlerError";
+  public readonly originalError: Error | undefined;
 
   constructor(message: string, originalError?: Error | null) {
     super(message);
     this.originalError = originalError || undefined;
   }
-
-  public hasOriginalError() {
-    return this.originalError !== undefined;
-  }
-
-  public getOriginalErrorMessage() {
-    return this.originalError ? this.originalError.message : undefined;
-  }
 }
diff --git a/src/result-handler.ts b/src/result-handler.ts
index 2bf582b4..cc36285a 100644
--- a/src/result-handler.ts
+++ b/src/result-handler.ts
@@ -108,8 +108,8 @@ export const lastResortHandler = ({
     .status(500)
     .end(
       `An error occurred while serving the result: ${error.message}.` +
-        (error.hasOriginalError()
-          ? `\nOriginal error: ${error.getOriginalErrorMessage()}.`
+        (error.originalError
+          ? `\nOriginal error: ${error.originalError.message}.`
           : "")
     );
 };


Test patch:
diff --git a/tests/unit/__snapshots__/open-api-helpers.spec.ts.snap b/tests/unit/__snapshots__/open-api-helpers.spec.ts.snap
index b8abef08..0dff6c87 100644
--- a/tests/unit/__snapshots__/open-api-helpers.spec.ts.snap
+++ b/tests/unit/__snapshots__/open-api-helpers.spec.ts.snap
@@ -44,7 +44,7 @@ exports[`Open API helpers depictDateIn should depict ZodDateIn 1`] = `
 }
 `;
 
-exports[`Open API helpers depictDateIn should throw when ZodDateIn in response 1`] = `[Error: Please use z.dateOut() for output.]`;
+exports[`Open API helpers depictDateIn should throw when ZodDateIn in response 1`] = `[OpenAPIError: Please use z.dateOut() for output.]`;
 
 exports[`Open API helpers depictDateOut should depict ZodDateOut 1`] = `
 {
@@ -57,7 +57,7 @@ exports[`Open API helpers depictDateOut should depict ZodDateOut 1`] = `
 }
 `;
 
-exports[`Open API helpers depictDateOut should throw when ZodDateOut in request 1`] = `[Error: Please use z.dateIn() for input.]`;
+exports[`Open API helpers depictDateOut should throw when ZodDateOut in request 1`] = `[OpenAPIError: Please use z.dateIn() for input.]`;
 
 exports[`Open API helpers depictDefault() should depict ZodDefault 1`] = `
 {
@@ -184,7 +184,7 @@ exports[`Open API helpers depictFile() should depict ZodFile 1`] = `
 }
 `;
 
-exports[`Open API helpers depictFile() should throw when using in input 1`] = `[Error: Please use z.file() only within ResultHandler.]`;
+exports[`Open API helpers depictFile() should throw when using in input 1`] = `[OpenAPIError: Please use z.file() only within ResultHandler.]`;
 
 exports[`Open API helpers depictIOExamples() should depict examples in case of request 1`] = `
 {
@@ -789,7 +789,7 @@ exports[`Open API helpers depictUpload() should depict ZodUpload 1`] = `
 }
 `;
 
-exports[`Open API helpers depictUpload() should throw when using in response 1`] = `[Error: Please use z.upload() only for input.]`;
+exports[`Open API helpers depictUpload() should throw when using in response 1`] = `[OpenAPIError: Please use z.upload() only for input.]`;
 
 exports[`Open API helpers depictZodBranded should depict the actual schema 1`] = `
 {
@@ -798,9 +798,9 @@ exports[`Open API helpers depictZodBranded should depict the actual schema 1`] =
 }
 `;
 
-exports[`Open API helpers depictZodDate should throw clear error 1`] = `[Error: Using z.date() within output schema is forbidden. Please use z.dateOut() instead. Check out the documentation for details.]`;
+exports[`Open API helpers depictZodDate should throw clear error 1`] = `[OpenAPIError: Using z.date() within output schema is forbidden. Please use z.dateOut() instead. Check out the documentation for details.]`;
 
-exports[`Open API helpers depictZodDate should throw clear error 2`] = `[Error: Using z.date() within input schema is forbidden. Please use z.dateIn() instead. Check out the documentation for details.]`;
+exports[`Open API helpers depictZodDate should throw clear error 2`] = `[OpenAPIError: Using z.date() within input schema is forbidden. Please use z.dateIn() instead. Check out the documentation for details.]`;
 
 exports[`Open API helpers excludeExampleFromDepiction() should remove example property of supplied object 1`] = `
 {
diff --git a/tests/unit/errors.spec.ts b/tests/unit/errors.spec.ts
index b44cb051..d565f7ac 100644
--- a/tests/unit/errors.spec.ts
+++ b/tests/unit/errors.spec.ts
@@ -8,60 +8,74 @@ import {
 describe("Errors", () => {
   describe("RoutingError", () => {
     test("should be an instance of Error", () => {
-      expect(new RoutingError("test")).toBeInstanceOf(RoutingError);
       expect(new RoutingError("test")).toBeInstanceOf(Error);
     });
+
+    test("should have the name matching its class", () => {
+      expect(new RoutingError("test").name).toBe("RoutingError");
+    });
   });
 
   describe("OpenAPIError", () => {
     test("should be an instance of Error", () => {
-      expect(new OpenAPIError("test")).toBeInstanceOf(OpenAPIError);
       expect(new OpenAPIError("test")).toBeInstanceOf(Error);
     });
+
+    test("should have the name matching its class", () => {
+      expect(new OpenAPIError("test").name).toBe("OpenAPIError");
+    });
   });
 
   describe("IOSchemaError", () => {
     test("should be an instance of Error", () => {
-      expect(new IOSchemaError("test")).toBeInstanceOf(IOSchemaError);
       expect(new IOSchemaError("test")).toBeInstanceOf(Error);
     });
+
+    test("should have the name matching its class", () => {
+      expect(new IOSchemaError("test").name).toBe("IOSchemaError");
+    });
   });
 
   describe("DependsOnMethodError", () => {
-    test("should be an instance of RoutingError", () => {
-      expect(new DependsOnMethodError("test")).toBeInstanceOf(
-        DependsOnMethodError
-      );
+    test("should be an instance of RoutingError and Error", () => {
       expect(new DependsOnMethodError("test")).toBeInstanceOf(RoutingError);
       expect(new DependsOnMethodError("test")).toBeInstanceOf(Error);
     });
+
+    test("should have the name matching its class", () => {
+      expect(new DependsOnMethodError("test").name).toBe(
+        "DependsOnMethodError"
+      );
+    });
   });
 
   describe("OutputValidationError", () => {
-    test("should be an instance of IOSchemaError", () => {
+    test("should be an instance of IOSchemaError and Error", () => {
       expect(new OutputValidationError("test")).toBeInstanceOf(IOSchemaError);
       expect(new OutputValidationError("test")).toBeInstanceOf(Error);
     });
+
+    test("should have the name matching its class", () => {
+      expect(new OutputValidationError("test").name).toBe(
+        "OutputValidationError"
+      );
+    });
   });
 
   describe("ResultHandlerError", () => {
     test("should be an instance of Error", () => {
-      expect(new ResultHandlerError("test")).toBeInstanceOf(ResultHandlerError);
       expect(new ResultHandlerError("test")).toBeInstanceOf(Error);
     });
 
-    test(".hasOriginalError() should depend on original error", () => {
-      const error = new ResultHandlerError("test", new Error("test2"));
-      expect(error.hasOriginalError()).toBeTruthy();
-      const error2 = new ResultHandlerError("test");
-      expect(error2.hasOriginalError()).toBeFalsy();
+    test("should have the name matching its class", () => {
+      expect(new ResultHandlerError("test").name).toBe("ResultHandlerError");
     });
 
-    test(".getOriginalErrorMessage() should depend on original error", () => {
+    test(".originalError should be the original error", () => {
       const error = new ResultHandlerError("test", new Error("test2"));
-      expect(error.getOriginalErrorMessage()).toBe("test2");
+      expect(error.originalError).toEqual(new Error("test2"));
       const error2 = new ResultHandlerError("test");
-      expect(error2.getOriginalErrorMessage()).toBeUndefined();
+      expect(error2.originalError).toBeUndefined();
     });
   });
 });