Skip to content

Instantly share code, notes, and snippets.

@radum
Created September 6, 2018 09:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save radum/ae21cb1dea24416e328ea1e5b98ab22d to your computer and use it in GitHub Desktop.
Save radum/ae21cb1dea24416e328ea1e5b98ab22d to your computer and use it in GitHub Desktop.
Creating Custom Error Objects In Node.js With Error.captureStackTrace()
// Require our core node modules.
var util = require( "util" );
// Export the constructor function.
exports.AppError = AppError;
// Export the factory function for the custom error object. The factory function lets
// the calling context create new AppError instances without calling the [new] keyword.
exports.createAppError = createAppError;
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// I create the new instance of the AppError object, ensureing that it properly
// extends from the Error class.
function createAppError( settings ) {
// NOTE: We are overriding the "implementationContext" so that the createAppError()
// function is not part of the resulting stacktrace.
return( new AppError( settings, createAppError ) );
}
// I am the custom error object for the application. The settings is a hash of optional
// properties for the error instance:
// --
// * type: I am the type of error being thrown.
// * message: I am the reason the error is being thrown.
// * detail: I am an explanation of the error.
// * extendedInfo: I am additional information about the error context.
// * errorCode: I am a custom error code associated with this type of error.
// --
// The implementationContext argument is an optional argument that can be used to trim
// the generated stacktrace. If not provided, it defaults to AppError.
function AppError( settings, implementationContext ) {
// Ensure that settings exists to prevent refernce errors.
settings = ( settings || {} );
// Override the default name property (Error). This is basically zero value-add.
this.name = "AppError";
// Since I am used to ColdFusion, I am modeling the custom error structure on the
// CFThrow functionality. Each of the following properties can be optionally passed-in
// as part of the Settings argument.
// --
// See CFThrow documentation: https://wikidocs.adobe.com/wiki/display/coldfusionen/cfthrow
this.type = ( settings.type || "Application" );
this.message = ( settings.message || "An error occurred." );
this.detail = ( settings.detail || "" );
this.extendedInfo = ( settings.extendedInfo || "" );
this.errorCode = ( settings.errorCode || "" );
// This is just a flag that will indicate if the error is a custom AppError. If this
// is not an AppError, this property will be undefined, which is a Falsey.
this.isAppError = true;
// Capture the current stacktrace and store it in the property "this.stack". By
// providing the implementationContext argument, we will remove the current
// constructor (or the optional factory function) line-item from the stacktrace; this
// is good because it will reduce the implementation noise in the stack property.
// --
// Rad More: https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi#Stack_trace_collection_for_custom_exceptions
Error.captureStackTrace( this, ( implementationContext || AppError ) );
}
util.inherits( AppError, Error );
try {
// ... some code that may throw an error.
} catch ( error ) {
switch ( error.type ) {
case "App.ThisError":
// ... handle this error.
break;
case "App.ThatError":
// ... handle that error.
break;
case "App.OtherError":
// ... handle other error.
break;
default:
// ... Hmm, unexpected error, rethrow it... or maybe do something
// else with it, like return a rejected promise.
throw( error );
break;
}
}
// Require our core node modules.
var util = require( "util" );
// Require our application node modules.
// --
// NOTE: I am renaming the createAppError() factory function to be appError(). I think
// this just makes the code a bit easier to read.
var appError = require( "./app-error" ).createAppError;
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// Try to call some code we know will throw an error.
try {
thisMethod();
// Output our custom error instance.
} catch ( error ) {
console.log( error.stack );
console.log( "Type: " + error.type );
console.log( "Message: " + error.message );
console.log( "Detail: " + error.detail );
console.log( "Extended Info: " + error.extendedInfo );
console.log( "Error Code: " + error.errorCode );
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// I throw a custom app error.
function thatMethod() {
throw(
appError({
type: "App.MissingArgument",
message: "You are missing an argument.",
detail: util.format( "The argument [%s] is required but was not passed-in.", "foo" ),
extendedInfo: "No! No weezing the joo-ooce!"
})
);
}
// I am here just to show nested call-stacks in the stacktrace.
function thisMethod() {
thatMethod();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment