Instantly share code, notes, and snippets.

Embed
What would you like to do?
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.
    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 cirtisim is welcomed.

@olegsu

This comment has been minimized.

Show comment
Hide comment
@olegsu

olegsu Feb 18, 2017

Just a small order fix

    // 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.
    Error.captureStackTrace(this, this.constructor);

This way the stacktrace will hold the name as well

olegsu commented Feb 18, 2017

Just a small order fix

    // 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.
    Error.captureStackTrace(this, this.constructor);

This way the stacktrace will hold the name as well

@fgarcia

This comment has been minimized.

Show comment
Hide comment
@fgarcia

fgarcia Apr 21, 2017

Extending Errors with ES6 classes has one minor downside: you are forced to use the new operator, while the native Error() object is a factory by itself.

Both of this statements work

throw Error()
throw new Error()

While this will fail

throw EmailTakenError();

But the approach is clearly the most modern solution

fgarcia commented Apr 21, 2017

Extending Errors with ES6 classes has one minor downside: you are forced to use the new operator, while the native Error() object is a factory by itself.

Both of this statements work

throw Error()
throw new Error()

While this will fail

throw EmailTakenError();

But the approach is clearly the most modern solution

@masiamj

This comment has been minimized.

Show comment
Hide comment
@masiamj

masiamj May 16, 2017

This looks awesome! I'm just having trouble actually capturing the stacktrace. Is this working for you?

masiamj commented May 16, 2017

This looks awesome! I'm just having trouble actually capturing the stacktrace. Is this working for you?

@gezza-b

This comment has been minimized.

Show comment
Hide comment
@gezza-b

gezza-b Jun 24, 2017

I really like this Error class approach and I added a print() method to the class to help me with error tracing.
However, it does not work when I move the error class to a separate module. Can I do that in the class or do I need a prototype?

gezza-b commented Jun 24, 2017

I really like this Error class approach and I added a print() method to the class to help me with error tracing.
However, it does not work when I move the error class to a separate module. Can I do that in the class or do I need a prototype?

@herlon214

This comment has been minimized.

Show comment
Hide comment
@herlon214

herlon214 Oct 5, 2017

Thanks! This is really what I wanted.

herlon214 commented Oct 5, 2017

Thanks! This is really what I wanted.

@momocow

This comment has been minimized.

Show comment
Hide comment
@momocow

momocow Nov 1, 2017

@gezza-b After moving the error class to a separate module, you should export the error class and use require to import it. Maybe you can provide an example here.

@fgarcia A wrapper function is needed if you want to handle the use with and without new.

errors/EmailTakenError.js

module.exports = function(){
    return new EmailTakenError()
}

Hoewever, using the new operator make it more clearly that it returns an instance of a class. (And it is only 3 letters longer)
It is all up to your preference, though.

momocow commented Nov 1, 2017

@gezza-b After moving the error class to a separate module, you should export the error class and use require to import it. Maybe you can provide an example here.

@fgarcia A wrapper function is needed if you want to handle the use with and without new.

errors/EmailTakenError.js

module.exports = function(){
    return new EmailTakenError()
}

Hoewever, using the new operator make it more clearly that it returns an instance of a class. (And it is only 3 letters longer)
It is all up to your preference, though.

@PaulSebi

This comment has been minimized.

Show comment
Hide comment
@PaulSebi

PaulSebi Nov 13, 2017

Hey, if you'd like an easy way to manage error Codes, check out snerr
npm
github

PaulSebi commented Nov 13, 2017

Hey, if you'd like an easy way to manage error Codes, check out snerr
npm
github

@wickd

This comment has been minimized.

Show comment
Hide comment
@wickd

wickd Dec 21, 2017

thank you, very useful :)

wickd commented Dec 21, 2017

thank you, very useful :)

@Maxtermax

This comment has been minimized.

Show comment
Hide comment
@Maxtermax

Maxtermax commented Jan 3, 2018

nice

@DaneTheory

This comment has been minimized.

Show comment
Hide comment
@DaneTheory

DaneTheory Mar 20, 2018

fantastic tutorial. Mind is blown on how slim the codebase is to pull this off. thanks!

DaneTheory commented Mar 20, 2018

fantastic tutorial. Mind is blown on how slim the codebase is to pull this off. thanks!

@izorsa

This comment has been minimized.

Show comment
Hide comment
@izorsa

izorsa May 15, 2018

nice, thanks!

izorsa commented May 15, 2018

nice, thanks!

@turakvlad

This comment has been minimized.

Show comment
Hide comment
@turakvlad

turakvlad 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.

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.

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