Graffiticode Error Handling Best Practices

The following principles should be used when writing error handling code:

  • Handle success / failure conditions before the stack clears. This means that any code that is asynchronous must catch exceptions just as it handles return value
  • Async code executed for side effect (e.g. upload a file) should handle exceptions and log error appropriately
  • HttpError error handlers send an http response
  • Non HttpError error handlers call next()
  • Define an subclass of Error for each error type
  • Use a helper function error() that takes an Error class and constructor arguments
  • Use a helper function assert() that takes a boolean and an Error object
const error = (errorClass, args) => new (errorClass)(...args);
const assert = (condition, error) => !condition && throw error;
import { AwesomeError } from '../errors';
...
assert(condition, error(AwesomeError, [x, y]));
1 Like

I like the simplicity and readability of asserts. I think this will reduce cognitive load on us and potentially other developers that visit the GC code base.

WDYT about creating a GC errors library with common errors and translators as well as “helpers” for specific errors

Common errors

  1. InvalidArgumentError
  2. UnauthorizedError
  3. IllegalStateError

The library would also have the common errors to Http response translator

const commonErrorHandler = (err, req, res, next) => {
if (err instanceof InvalidArgumentError) {
res.status(400).send(err.message);
}
// Etc…
}

And helpers

const checkArgument = (condition, message) => assert(condition, InvalidArgumentError, [message])

Having a shared library sounds like a good idea.

This may be implied in what you wrote, but we might want to have a complete set of base classes that map out the space of possible handler actions, either with an http response or logging.

E.g. all errors that cause a 400 to be sent would inherit the Http400Error (class InvalidArgumentError extends Http400Error). Then we could check for those more abstract classes when handling errors.

From a DX perspective, this reduces error handling to defining an error class and writing an assert(). This will go along way to encouraging the creation of more detailed and descriptive error reporting.