Skip to content

Instantly share code, notes, and snippets.

@ceejbot
Created August 24, 2012 04:32
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 ceejbot/3445447 to your computer and use it in GitHub Desktop.
Save ceejbot/3445447 to your computer and use it in GitHub Desktop.
An error-handling pattern for node.js
function functionThatMightThrow(input)
{
return JSON.parse(input);
}
function putTogetherSomeData(input, callback)
{
var result = {};
redis.get(input, function(err, reply)
{
if (err)
return callback(err);
if (!reply)
return callback(new Error('data not found'));
result.id = reply;
couch.get(reply, function(err, response, body)
{
if (err)
return callback(err);
if (response.statusCode === 404)
return callback(null, {});
if (response.statusCode !== 200)
return callback(new Error('unexpected status code ' + response.statusCode));
try
{
result.payload = functionThatMightThrow(body);
}
catch (err)
{
return callback(err, result);
}
callback(null, result);
});
});
}
function fetchFromRedis(key, callback, errh)
{
redis.get(key, function(err, reply)
{
if (err)
return errh(err);
if (!reply)
return errh(new Error('data not found'));
callback(reply);
});
}
function fetchFromCouchDB(key, callback, errh)
{
couch.get(key, function(err, response, body)
{
if (err)
return errh(err);
if (response.statusCode === 404)
return callback({});
if (response.statusCode !== 200)
return errh(new Error('unexpected status code ' + response.statusCode));
callback(body);
});
}
function functionThatMightThrow(input)
{
return JSON.parse(input);
}
function putTogetherSomeData(input, callback)
{
var result = {};
var errorHandler = function(err)
{
logger.error('oh the humanity: ' + JSON.stringify(err));
// clean up any allocated resources
// roll back any half-complete actions
return callback(err);
};
fetchFromRedis(input, function(reply)
{
result.id = reply;
fetchFromCouchDB(reply, function(body)
{
try
{
result.payload = functionThatMightThrow(body);
}
catch (err)
{
return errorHandler(err);
}
callback(null, result);
}, errorHandler);
}, errorHandler);
}
var assert = require('assert');
function wrapErrReplyPattern(func)
{
var wrapper = function()
{
var arglen = arguments.length;
assert(arglen > 1, 'this function requires success & failure callbacks');
var errh = arguments[arglen - 1];
assert(typeof errh === 'function', 'error callback must be a function');
var success = arguments[arglen - 2];
assert(typeof success === 'function', 'success callback must be a function');
var callback = function(err, reply)
{
if (err)
return errh(err);
if (!reply)
return errh(new Error('data not found'));
success(reply);
};
var params = Array.prototype.slice.call(arguments, 0, arglen - 2);
params.push(callback);
func.apply(null, params);
};
return wrapper;
}
function wrapErrResponseBodyPattern(func, expected_statuses)
{
var wrapper = function()
{
var arglen = arguments.length;
assert(arglen > 1, 'this function requires success & failure callbacks');
var errh = arguments[arglen - 1];
assert(typeof errh === 'function', 'error callback must be a function');
var success = arguments[arglen - 2];
assert(typeof success === 'function', 'success callback must be a function');
var callback = function(err, response, body)
{
if (err)
return errh(err);
if (expected_statuses && expected_statuses.indexOf(response.statusCode) === -1)
return errh(new Error('unexpected status code ' + response.statusCode));
success(response, body);
};
var params = Array.prototype.slice.call(arguments, 0, arglen - 2);
params.push(callback);
func.apply(null, params);
};
return wrapper;
}
var fetchFromCouchDB = wrapErrResponseBodyPattern(couch.get, [200, 304, 404]);
var fetchFromRedis = wrapErrReplyPattern(redis.get);
function functionThatMightThrow(input)
{
return JSON.parse(input);
}
function putTogetherSomeData(input, callback)
{
var result = {};
var errorHandler = function(err)
{
// log, clean up, roll back, etc
return callback(err);
};
fetchFromRedis(input, function(reply)
{
result.id = reply;
fetchFromCouchDB(reply, function(response, body)
{
// application level can look at response.statusCode to make decisions
try
{
result.payload = functionThatMightThrow(body);
}
catch (err)
{
return errorHandler(err);
}
callback(null, result);
}, errorHandler);
}, errorHandler);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment