Created
August 24, 2012 04:32
-
-
Save ceejbot/3445447 to your computer and use it in GitHub Desktop.
An error-handling pattern for node.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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