JS Error Handling

Todays Menu

  1. Error Object Definition
  2. Error Best Practices
  3. Extending the Error Object
  4. Practical uses in Node.js
  5. Using Errors at your API

The Error Constructor


new Error([message[, fileName[, lineNumber]]])
function equals(a, b) {
  if (a !== b) {
    throw new Error('Values are not equal');
  }
}

Error Properties

Standard

  • message The error message.
  • name The error name.

Mozilla (node.js)

  • fileName Path to file that raised the error.
  • lineNumber The line number in the file that raised the error.
  • columnNumber The col number.
  • stack The error execution stack.

Error Methods

  • toString() Returns a string representing the Error Object, overrides the Object.prototype.toString() method.

Error Types

EvalError Creates an instance representing an error that occurs regarding the global function eval().
InternalError  Creates an instance representing an error that occurs when an internal error in the JavaScript engine is thrown. E.g. "too much recursion".
RangeError Creates an instance representing an error that occurs when a numeric variable or parameter is outside of its valid range.
ReferenceError Creates an instance representing an error that occurs when de-referencing an invalid reference.
SyntaxError Creates an instance representing a syntax error that occurs while parsing code in eval().
TypeError Creates an instance representing an error that occurs when a variable or parameter is not of a valid type.
URIError Creates an instance representing an error that occurs when encodeURI() or decodeURI() are passed invalid parameters.

Javascript Error Best Practises

General Advice

Always be using Error Objects

More Specifically

 

  • In your [node.js] callbacks
  • In error events you emit
  • In your API (normalize)
  • Throw in your Promises

Extending Error

Your own Error Objects

Classical Inheritance?

var util = require('util');

var CustomError = function() {
  Error.apply(this, arguments);

  this.customProp = true;
};
util.inherits(CustomError, Error);

console.log(customError.message); // undefined
console.log(customError.stack); // empty response
console.log(customError instanceof Error); // true
console.log(Object.keys(customError)); // ['customProp']

What happened here?

var util = require('util');

var CustomError = function() {
  Error.apply(this, arguments);

  this.customProp = true;
};
util.inherits(CustomError, Error);

Error is a function that returns a new Error object and does not manipulate this in any way

How to extend Error (node.js)

var util = require('util');

var CustomError = function(message) {
  Error.captureStackTrace(this, this.constructor);
  this.name = 'CustomError';
  this.message = message;
};
util.inherits(CustomError, Error);
  • instanceof Error works
  • Stack traces work as expected
  • Can extend itself
  • captureStackTrace() is v8 specific
  • For web replace with local throw

How to extend Error (web)

var util = require('util');

var CustomError = function(message) {
    // Use V8's native method if available, otherwise fallback
    if ('captureStackTrace' in Error) {
        Error.captureStackTrace(this, CustomError);
    } else {
        this.stack = (new Error()).stack;
    }

  this.name = 'CustomError';
  this.message = message;
};
util.inherits(CustomError, Error);
  • instanceof Error works
  • Stack traces have actual file on second line
  • Can extend itself

Errors in Node.js

"Signing" Errors

Errors in Node.js

errors.DatabaseError = function(message) {
  Error.captureStackTrace(this, this.constructor);
  this.name = 'AcmeDatabaseError';
  this.message = message;

  // custom props
  this.error = true;
};
util.inherits(errors.DatabaseError, Error);

Augmented Errors

Errors in Node.js

var appError = require('app-errors');

// ... //

User.prototype.hasPrivilege = function(userId, privilege, cb) {
    this.fetchUser(userId, function(err, udo) {
        if (!udo) {
            var err = new appError.EmptyResultsError('User not found');
            err.userId = userId;
            cb(err);
            return;
        }
        // ... //
    });
};

Catch Specific Errors

Errors in Node.js

try {
    myroutine(); // may throw three types of exceptions
} catch (ex) {
    if (ex instanceof TypeError) {

    } else if (ex instanceof RangeError) {

    } else if (ex instanceof EvalError) {

    } else {
       logMyErrors(ex);
    }
}

Using Errors on API

how to work with express

The Overview

Using Errors on API

  • Create custom Errors with an httpCode or similar attribute, set to 500 by default.
  • Throw them or augment existing Error objects by defining the httpCode.
  • Create an express error middleware handling those errors.
  • Pipe all error handling to next

Pipe Errors to next

Using Errors on API

UserCtrl.prototype.fetchUser = function(req, res, next) {
    this.userEnt.readOne(req.param.userId)
        .then(res.json)
        .catch(next);
};

Express Error Handler

Using Errors on API

var customErrorHandler = function (err, req, res, next) {
  var httpCode = 500;

  httpCode = err.httpCode || httpCode;

  res.status(httpCode);

  // figure out what the client accepts
  if (helpers.isRequestJson(req)) {
    res.json(error);
  } else {
    res.render('error/' + error.httpCode, {error: error});
  }
};

// Express error handler must always be last
// in all of your middleware
app.use(customErrorHandler);

Thank you

Thanasis Polychronakis

@thanpolas

https://speakerdeck.com/thanpolas

Questions?

Thanasis Polychronakis

@thanpolas

https://speakerdeck.com/thanpolas

Made with Slides.com