Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A very minimal Javascript (ES5 & ES6) Middleware Pattern Implementation
var Middleware = function() {};
Middleware.prototype.use = function(fn) {
var self = this;
this.go = (function(stack) {
return function(next) {
stack.call(self, function() {
fn.call(self, next.bind(self));
});
}.bind(this);
})(this.go);
};
Middleware.prototype.go = function(next) {
next();
};
class Middleware {
use(fn) {
this.go = (stack => next => stack(fn.bind(this, next.bind(this))))(this.go);
}
go = next => next();
}
// Inspired by: https://github.com/kolodny/exercises/tree/master/middleware
var middleware = new Middleware();
middleware.use(function(next) {
var self = this;
setTimeout(function() {
self.hook1 = true;
next();
}, 10);
});
middleware.use(function(next) {
var self = this;
setTimeout(function() {
self.hook2 = true;
next();
}, 10);
});
var start = new Date();
middleware.go(function() {
console.log(this.hook1); // true
console.log(this.hook2); // true
console.log(new Date() - start); // around 20
});
@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Dec 5, 2016

what does the setTimeout provide in these cases?

@djleonskennedy

This comment has been minimized.

Copy link

@djleonskennedy djleonskennedy commented Dec 7, 2016

@Cratyz it shows that pipe works with async stuff

@unbug

This comment has been minimized.

Copy link

@unbug unbug commented Dec 23, 2016

This is a really small one! Nice done! Here is a powerful Javascript Middleware Pattern Implementation

@yejinjian

This comment has been minimized.

Copy link

@yejinjian yejinjian commented Feb 25, 2017

nice!

@sylvainleris

This comment has been minimized.

Copy link

@sylvainleris sylvainleris commented Aug 9, 2017

Great job ! Here a modified version to pass arguments.

@igordeoliveirasa

This comment has been minimized.

Copy link

@igordeoliveirasa igordeoliveirasa commented Sep 5, 2017

how to pass multiple functions with 'next' object? Like expressJS ?

@shmuelgutman

This comment has been minimized.

Copy link

@shmuelgutman shmuelgutman commented Dec 4, 2017

Hi,

What refactoring should I do for this middleware in order to be able to pass (err, res) for each next in turn?
Meaning something like next(err, res) so that at the end of the middleware stack execution I have some result object

@Curtis017

This comment has been minimized.

Copy link

@Curtis017 Curtis017 commented Mar 8, 2019

Nice work! inspired me to make my own using typescript https://gist.github.com/Curtis017/ce072208f5160e8c24b77cc662f10fec

@hieusmiths

This comment has been minimized.

Copy link

@hieusmiths hieusmiths commented Jun 15, 2020

I hope you can write and explain detail it. Thank so much.

@soufyakoub

This comment has been minimized.

Copy link

@soufyakoub soufyakoub commented Jul 25, 2020

@darrenscerri Thanks for this nice piece of code, it helped me learn about call stacks and closures.

I just wanted to point out that this bind is unnecessary since it's not used inside the bound function.

What was the intention behind using it anyway?
the "this" in this case refers to the global object, maybe you were trying to bind self instead?

@aronanda

This comment has been minimized.

Copy link

@aronanda aronanda commented Sep 14, 2020

Well done, it took me a bit to "wrap" my head around.
Here's my version with parameters, optional object definition and you can pipe the use() method:
https://gist.github.com/aronanda/d31bb47918145a5aace6005f172e035d

class Middleware {
  constructor(obj) {
    obj = obj || this
    Object.defineProperty(this, '__obj', { value: obj })
    Object.defineProperty(this, 'go', { value: function go(...args) {
      args[args.length - 1].apply(obj, args.slice(0, -1))
    }, writable: true })
  }
  use(fn) {
    this.go = (stack => (...args) => stack(...args.slice(0, -1), () =>
      fn.call(this.__obj, ...args.slice(0, -1), args[args.length - 1]
        .bind(this.__obj, ...args.slice(0, -1)))))(this.go)
    return this
  }
}

// Example:
const middleware = new Middleware(/* define object or defaults to this */)

middleware.use(function (req, res, next) {
  setTimeout(() => {
    this.hook1 = true
    req.step++
    res.step++
    next()
  }, 10)
})

middleware.use(function (req, res, next) {
  setTimeout(() => {
    this.hook2 = true
    req.step++
    res.step++
    next()
  }, 10)
}).use((req, res, next) => {
  // chainable
  setTimeout(() => {
    req.step++
    res.step++
    next()
  }, 10)
})

const start = new Date()
const req = Object.create(new class Req {}(), { step: { value: 1, enumerable: true, writable: true }})
const res = Object.create(new class Res {}(), { step: { value: 1, enumerable: true, writable: true }})
middleware.go(req, res, function (req, res) {
  console.log(this) // Middleware { hook1: true, hook2: true }
  console.log(req) // Req { step: 4 }
  console.log(res) // Res { step: 4 }
  console.log(new Date() - start) // around 30ms
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment