Skip to content

Instantly share code, notes, and snippets.

@slavafomin
Last active March 9, 2024 12:03
Show Gist options
  • Save slavafomin/b164e3e710a6fc9352c934b9073e7216 to your computer and use it in GitHub Desktop.
Save slavafomin/b164e3e710a6fc9352c934b9073e7216 to your computer and use it in GitHub Desktop.
Custom ES6 errors in Node.js

Here's how you could create custom error classes in Node.js using latest ES6 / ES2015 syntax.

I've tried to make it as lean and unobtrusive as possible.

Defining our own base class for errors

errors/AppError.js

module.exports = class AppError extends Error {
  constructor (message, status) {
  
    // Calling parent constructor of base Error class.
    super(message);
    
    // Saving class name in the property of our custom error as a shortcut.
    this.name = this.constructor.name;

    // Capturing stack trace, excluding constructor call from it
    // (this is probably no longer required in node >=8, see the comments)
    Error.captureStackTrace(this, this.constructor);
    
    // You can use any additional properties you want.
    // I'm going to use preferred HTTP status for this error types.
    // `500` is the default value if not specified.
    this.status = status || 500;
    
  }
};

Defining specific error types

errors/EmailTakenError.js

module.exports = class EmailTakenError extends require('./errors/AppError') {
  constructor (message) {
    // Providing default message and overriding status code.
    super(message || 'Specified E-Mail is already taken', 400);
  }
};

errors/RequestValidationError.js

module.exports = class extends require('./AppError') {
  constructor (fields) {
    // Overriding both message and status code.
    super('Request validation failed', 400);
    // Saving custom property.
    this.fields = fields || {};
  }
};

Throwing and catching

const AppError = require('./../api/errors/AppError');
const EmailTakenError = require('./../api/errors/EmailTakenError');
const RequestValidationError = require('./../api/errors/RequestValidationError');


try {
  // Throwing EmailTakenError exception.
  throw new EmailTakenError();
} catch (error) {
  // Catching exception by class.
  if (error instanceof EmailTakenError) {
    console.log('E-Mail validation failed!', error);
  } else {
    // If failed to catch, throwing it higher.
    console.log('Unknown error', error);
    throw error;
  }
}


try {
  // Throwing RequestValidationError exception.
  throw new RequestValidationError();
} catch (error) {
  // Catching error by base (parent) class.
  if (error instanceof AppError) {
    console.log('Some application error occurred!', error);
  } else {
    console.log('Unknown error', error);
    throw error;
  }
}

Feedback

It works great for my application, however criticism is welcomed.

@izorsa
Copy link

izorsa commented May 15, 2018

nice, thanks!

@turakvlad
Copy link

turakvlad commented Aug 22, 2018

I've run your code a few times and noticed that in the Node.js 8.11.2 I'm getting the same output with and without Error.captureStackTrace(this, this.constructor); line in the AppError.js constructor.

It looks like in the Node.js 8.11.2 and above it's the default behavior. If we're extending Error class, we don't need to use captureStackTrace to omit all frames above this.constructor.

Please, take a look at the documentation. The most important part:

The error.stack property will represent the point in the code at which new Error() was called. Stack traces are dependent on V8's stack trace API. Stack traces extend only to either (a) the beginning of synchronous code execution, or (b) the number of frames given by the property Error.stackTraceLimit, whichever is smaller.

.stack will be added automatically.

Please let me know if I am not correct.

@bautigaraventa
Copy link

I've run your code a few times and noticed that in the Node.js 8.11.2 I'm getting the same output with and without Error.captureStackTrace(this, this.constructor); line in the AppError.js constructor.

It looks like in the Node.js 8.11.2 and above it's the default behavior. If we're extending Error class, we don't need to use captureStackTrace to omit all frames above this.constructor.

Please, take a look at the documentation. The most important part:

The error.stack property will represent the point in the code at which new Error() was called. Stack traces are dependent on V8's stack trace API. Stack traces extend only to either (a) the beginning of synchronous code execution, or (b) the number of frames given by the property Error.stackTraceLimit, whichever is smaller.

.stack will be added automatically.

Please let me know if I am not correct.

I'm going to implement this solution. Is it confirmed what you say about the captureStackTrace?

Thanks in advance!

@raghavendra7992
Copy link

class ErrorHandler extends Error{
constructor(message,statusCode){
super(message);
this.statusCode = statusCode||500;
Error.captureStackTrace(this,this.constructor);

}

};
I used this as an error handler but it does not give output for wrong input means login authentication

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment