Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save op1ekun/b645fd91728a954c3cb37eb53c56ba07 to your computer and use it in GitHub Desktop.
Save op1ekun/b645fd91728a954c3cb37eb53c56ba07 to your computer and use it in GitHub Desktop.
// Minimal middleware implementation
// Works in node and the browser
// Inspired by https://github.com/h2non/midware
// Usage examples are at the bottom...
function middleware(ctx) {
let calls = [];
let errorHandlers = [];
var ctx = ctx || null;
function addMiddleware(...args) {
// Optimize for the most common case
if (args.length === 1 && typeof args[0] === 'function') {
this.push(args[0]);
return;
}
// Handle the general case of many functions passed in
args
.filter(fn => { return typeof fn === 'function'; })
.forEach(fn => { this.push(fn); });
}
function use() {
addMiddleware.apply(calls, Array.prototype.slice.call(arguments));
}
function err(...args) {
addMiddleware.apply(errorHandlers, Array.prototype.slice.call(arguments));
}
function run(...args) {
var done;
let stack = calls.slice();
var lastExecutedFn;
if (typeof args[args.length - 1] === 'function') {
done = args.pop();
}
if (!stack.length) {
callDone();
return;
}
args.push(next);
function exec() {
try {
lastExecutedFn = stack.shift();
lastExecutedFn.apply(ctx, args);
}
catch (e) {
// Call next() function with an error
next(e);
}
}
function callDone(error) {
if (done) {
done.call(ctx, error);
}
}
function next(error, end) {
if (end || !stack.length) {
stack = null;
callDone(error);
return;
}
if (error) {
args.push(error);
stack = errorHandlers.slice();
}
exec();
}
exec();
// Detect missing call to next
if (stack !== null && typeof lastExecutedFn === 'function') {
console.warn(`Missing call to next()? Use next(null, true) to stop further processing in middleware: \n${lastExecutedFn.name == '' ? lastExecutedFn.toString() : lastExecutedFn.name}`);
}
};
return {
use,
err,
run
};
}
// Using a context is optional
var context = { entity: 'test' };
// Create an instance of middleware
var app = middleware(context);
// CASE 1: Well-behaved middleware function
app.use(function (next) {
console.log(this.entity);
next();
});
// CASE 2: Multiple well-behaved middleware functions in a single declaration
app.use(function (next) {
console.log('Thing 1');
next();
}, function (next) {
console.log('Thing 2');
next();
});
// CASE 3: Middleware signals error and prevents further execution
app.use(function (next) {
next('Error');
});
// CASE 4: Middleware throws error and prevents further execution
app.use(function (next) {
throw new Error('Test');
next();
});
// CASE 5: Missing next() call prevents further execution
app.use(function (next) {
// missing next();
});
// CASE 6: Error handler processes the error and passes it on
app.err(function errLogger(next, err) {
console.error(err);
next(err);
});
app.run(function finalizer(err) {
console.log(typeof err === 'undefined' ? 'Success or handled error' : 'Error');
});
// Middleware with parameters
var app2 = middleware();
app2.use(function (req, res, next) {
console.log(req, res);
next();
});
app2.run({ url: 'http...' }, { status: 200 }, function finalizer2(err) {
console.log(typeof err === 'undefined' ? 'Success' : 'Error');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment