Git Product home page Git Product logo

meddleware's Introduction

meddleware

Build Status

Configuration-based middleware registration for express.

Note: meddleware >=1.0 is only compatible with express >=4.0. For express 3.x support, please use meddleware 0.1.x.

Usage

var http = require('http'),
    express = require('express'),
    meddleware = require('meddleware'),
    config = require('shush')('./config/middleware');

var app = express();
app.use(meddleware(config)); // or app.use('/foo', meddleware(config));
http.createServer(app).listen(8080);

Configuration

meddleware configuration consists of an object containing properties for each of the middleware to be registered.

{
    "favicon": {
        "enabled": true,
        "priority": 10,
        "module": "static-favicon"
    },

    "static": {
        "enabled": true,
        "priority": 20,
        "module": {
            "name": "serve-static",
            "arguments": [ "public" ]
        }
    },

    "custom": {
        "enabled": true,
        "priority": 30,
        "route": "/foo",
        "module": {
            "name": "./lib/middleware",
            "method": "customMiddleware",
            "arguments": [ "foo", { "bar": "baz" } ]
        }
    },

    "security": {
        "enabled": true,
        "priority": 40,
        "route": [ "/foo", "/bar" ],
        "module": {
            "name": "./lib/security",
            "arguments": [ { "maximum": true } ]
        }
    },

    "cookieParser": {
        "enabled": false,
        "priority": 50,
        "module": {
            "name": "cookie-parser",
            "arguments": [ "keyboard cat" ]
        }
    },

    "misc": {
        "priority": 60,
        "parallel": {
            "user": {
                "enabled": true,
                "module": {
                    "name": "the-module",
                    "arguments": []
                }
            },
        }
    }
}

Options

  • enabled (boolean) - Set to true to enable middleware, false to disable. Defaults to true.

  • priority (number) - The weight to give a particular piece of middleware when sorting for registration. Lower numbers are registered first, while higher numbers are registered later. If priority is not a number, this setting defaults to Number.MIN_VALUE.

  • module (object or string) - The name or definition of the module to load containing the middleware implementation. Can be an installed module or a path to a module file within your project/application.

    • name (string) - The name of the module or path to local module.

    • method (string, optional) - The method on the provided module upon which invocation will create the middleware function to register. If a factory method is not provided, it defaults to the name of the current middleware being processed, and finally back to the module itself.

    • arguments (array, optional) - An array of arguments to pass to the middleware factory.

  • route (string or array or regexp, optional) - An express route against which the middleware should be registered. Can be a string or a regular expression, or an array consisting of strings and regular expressions.

    Caveats
    1. If configuring meddleware with json files, you'll need to use something like shortstop with shortstop-regex to convert a string to RegExp.

    2. String paths will be automatically prefixed with any mountpath but regular expressions will not.

    var config = {
      myMiddleware: {
        module: './myMiddleware',
        route: '/foo'
      },
      otherMiddleware: {
        module: './otherMiddleware',
        route: /^\/bar$/i
      }
    }
    
    app.use('/baz', meddleware(config));
    
    // `myMiddleware` will run on `/baz/foo`
    // `otherMiddleware` will run on `/bar`
  • parallel (object, optional) - A meddleware configuration object containing middleware which should be executed in parallel, proceeding only when all have completed.

  • race (object, optional) - A meddleware configuration object containing middleware which should be executed in parallel, but will proceed when the first one completes.

  • fallback (object, optional) - A meddleware configuration object containing middleware which should be executed sequentially, proceeding upon first successfully resolved middleware, falling back in the event of a failure.

Express App Events

Along with registration, consumers can be notified of registration events. NOTE: These events are only triggered for the middleware that is registered via meddleware. All middleware events receive the following eventargs object:

{
   app: [object Object], // express app
   config: [object Object] // config object for the current middleware
}

There are 4 types of events one can subscribe to:

  • middleware:before - Subscribe to this event to be notified immediately before every middleware registration. The event handler will receive an eventargs object containing 2 properties: app being the express application against which the middleware was registered, and config being the configuration object used in registering the middleware.

  • middleware:before:{name} - Subscribe to this event to be notified immediately before registration of the named middleware. The event handler will receive an eventargs object containing 2 properties: app being the express application against which the middleware was registered, and config being the configuration object used in registering the middleware.

  • middleware:after - Subscribe to this event to be notified immediately after every middleware registration. The event handler will receive an eventargs object containing 2 properties: app being the express application against which the middleware was registered, and config being the configuration object used in registering the middleware.

  • middleware:after:{name} - Subscribe to this event to be notified immediately after registration of the named middleware. The event handler will receive an eventargs object containing 2 properties: app being the express application against which the middleware was registered, and config being the configuration object used in registering the middleware.

var express = require('express'),
    meddle = require('meddleware'),
    config = require('shush')('./config/middleware');

app = express();

app.on('middleware:before', function (eventargs) {
    console.log(eventargs.config.name); // depends on which middleware is about to be registered
});

app.on('middleware:before:session', function (eventargs) {
    console.log(eventargs.config.name); // 'session'
});

app.on('middleware:after:session', function (eventargs) {
    console.log(eventargs.config.name); // session
});

app.on('middleware:after', function (eventargs) {
    console.log(eventargs.config.name); // depends on which middleware is about to be registered
});

app.use(meddle(config));

Middleware Flow Control

To manage groups of middleware, there is support for parallel, race, and fallback, which allow you to register middleware intended to be run using each type of flow control. Additionally, these registration types are composable.

Parallel

Middleware designated as parallel will all be executed simultaneously, continuing processing of the remaining middleware stack only when all have completed.

{
     "cookieParser": {
         "enabled": false,
         "priority": 10,
         "module": {
            "name": "cookie-parser",
            "arguments": [ "keyboard cat" ]
        }
     },

    "setup": {
        "enabled": true,
        "priority": 20,
        "parallel": {
            "service1": {
                "enabled": true,
                "module": "./lib/middleware/service1"
            },
            "service2": {
                "enabled": true,
                "module": "./lib/middleware/service2"
            },
            "service3": {
                "enabled": true,
                "module": "./lib/middleware/service3"
            }
        }
    },

    "json": {
        "enabled": true,
        "priority": 30,
        "module": {
            "name": "body-parser",
            "method": "json"
        }
    }
Race

Middleware designated as race will all be executed simultaneously, continuing processing of the remaining middleware stack when the first has completed.

{
     "cookieParser": {
         "enabled": false,
         "priority": 10,
         "module": {
            "name": "cookie-parser",
            "arguments": [ "keyboard cat" ]
         }
     },

    "setup": {
        "enabled": true,
        "priority": 20,
        "race": {
            "service1a": {
                "enabled": true,
                "module": "./lib/middleware/service1a"
            },
            "service1b": {
                "enabled": true,
                "module": "./lib/middleware/service1b"
            }
        }
    },

    "json": {
        "enabled": true,
        "priority": 30,
        "module": {
            "name": "body-parser",
            "method": "json"
        }
    }
Fallback

Middleware designated as fallback will execute each middleware in series until one completes successfully.

{
     "cookieParser": {
         "enabled": false,
         "priority": 10,
         "module": {
             "name": "cookie-parser",
             "arguments": [ "keyboard cat" ]
         }
     },

    "setup": {
        "enabled": true,
        "priority": 20,
        "fallback": {
            "primaryService": {
                "enabled": true,
                "priority": 10,
                "module": "./lib/middleware/primaryService"
            },
            "secondaryService": {
                "enabled": true,
                "priority": 20,
                "module": "./lib/middleware/secondaryService"
            }
        }
    },

    "json": {
        "enabled": true,
        "priority": 30,
        "module": {
            "name": "body-parser",
            "method": "json"
        }
    }

Tests

$ npm test

Coverage

$ npm run cover && open coverage/lcov-report/index.html

meddleware's People

Contributors

alexsantos avatar aredridel avatar danbehar avatar gabrielcsapo avatar grawk avatar jasisk avatar jeffharrell avatar johnkchiu avatar luizfilipe avatar montague avatar osukaa avatar paulomcnally avatar pvenkatakrishnan avatar shaunwarman avatar sumeetkakkar avatar totherik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

meddleware's Issues

problems specifying exclusive routes

Trying to ensure a middleware does not get executed in a few routes (/developer/login, /developer/coverage) where the requestURI is "/developer". I specify "route" property for the middleware as "/(?!login|coverage)" which yields the following error:

Tue, 03 Jun 2014 15:19:52 GMT uncaughtException Invalid regular expression: /^/developer/(?:?!login|coverage)/?(?=/|$)/: Nothing to repeat

I've tried other permutations (?!(login|coverage)), etc. which either yield the same error or the middleware never runs on any route.

-Chris

meddleware broken with config v.3.0

config package made its returned objects sealed on version 3.0. This breaks meddleware:

$ node maps.js 
/tmp/map-server/node_modules/meddleware/lib/util.js:50
    obj && (obj.name = name);
                     ^

TypeError: Cannot add property name, object is not extensible
    at nameObject (/tmp/map-server/node_modules/meddleware/lib/util.js:50:22)
    at /tmp/map-server/node_modules/meddleware/lib/util.js:38:16
    at Array.map (<anonymous>)
    at Object.mapValues (/tmp/map-server/node_modules/meddleware/lib/util.js:36:29)
    at Function.onmount (/tmp/map-server/node_modules/meddleware/index.js:196:14)
    at Object.onceWrapper (events.js:315:30)
    at emitOne (events.js:121:20)
    at Function.emit (events.js:211:7)
    at Function.<anonymous> (/tmp/map-server/node_modules/express/lib/application.js:238:8)
    at Array.forEach (<anonymous>)

There is a work around given on this config issue, however having immutable and sealed objects on require('config') is desirable.

Do you think meddleware can do something about it?

Question: what's the recommended way to set a custom store for sessions?

In previous versions of kraken, I set the "module" property in the session config to specify a custom module to use to create session stores. With meddleware, it seems I need to instantiate the store myself at runtime and tack it on to the sessions config before the session store is created. Is that right, or is there another way to do this?

var PostgresStore = require('./lib/postgres-sessions');
app.on('middleware:before:sessions', function(args) {
  args.config.store = new PostgresStore;
});

Deprecate RQ for parallelism

RQ seems to have been written before libraries such as Bluebird were available. Since Meddleware isn't using one of the core features of RQ (timeouts), wouldn't it be better to adopt a more standard approach to handling asynchronous actions?

Route on middleware not working

I created a yo kraken basic app to demonstrate. I added a splitTraffic middleware item to config.json and it prints out if it is ever called. It activates on the route /add (assuming I got the regex syntax straight).

Running the app and hitting /add should print the message from the middleware and the message from the route controller. All I see is the route controller. If I remove, the route on the middleware, then both show up.

Here is the sample showing it.
https://github.com/rragan/testroute

`module.name` is not required?

According to the documentation, the module.name option is required, which makes sense. However, in Kraken's implementation, I see this:

"router": {
    "module": {
        "arguments": [{ "directory": "path:./controllers" }]
    }
}

Could someone please provide some insight into what's going on here? To what module are the arguments that we see here being passed?

Initializing PassportJS

I am trying but failing to register PassportJS middleware in the config file. Here is the config file code for meddleware:

// passportjs
"passport": {
   "enabled": true,
   "priority": 92,
   "module": {
      "name": "passport",
      "method": "initialize"
   }
},
// passportjs
"passport": {
   "enabled": true,
   "priority": 93,
   "module": {
      "name": "passport",
      "method": "session"
   }
}

What am I doing wrong ?

normalize the config `module` property

closed: won't matter for confit since they're independent steps. NM.

the module property can either be shorthanded to a string or an object with name and arguments properties. We should normalize this so, i.e., confit can override arguments even if an upstream config uses the shorthand.

In other words:

{
  "myMiddleware": {
    "module": "./lib/myMiddleware"
  }
}
app.on('middleware:after:myMiddleware', function (data) {
  assert(data.spec.module.name === './lib/myMiddleware');
});

Adding routes in meddleware

Hi,

I'm developing a middleware to authenticate routes and also have a callback url after authentication.

I would like to add that callback route as part of middleware file

something like this.

app.get('/authenticateCallbackurl',function(){
 // redirect 
})

Also do you have custom meddleware examples

Question: Loading I18next

How can i do this in meddleware?

var express = require('express')
, i18n = require('i18next');

i18n.init({
saveMissing: true,
debug: true
});

var app = express();

app.use(i18n.handle);

i18n.registerAppHelper(app);

edge case bug in the local path resolver

Plonk.

tryRequire just calls require.resolve which will, for local files, start relative to the file that contains it.

Therefore, if you try to require a file with a relative path that could resolve to a similarly named file from the util folder in meddleware, ๐Ÿ’ฅ.

Proof:

$ echo "console.log('i am required');" > rq.js

$ cat - > index.js <<EOF
const meddleware = require('meddleware');
const express = require('express');

const app = express();

const config = {
  example: {
    module: './rq.js'
  }
};

app.use(meddleware(config));
EOF

$ node index.js
/cat/city/secret/path/node_modules/meddleware/index.js:100
            throw new Error('Unable to locate middleware in ' + config.name);
            ^

Error: Unable to locate middleware in ./rq.js

How can I have an environment-controllable value that defaults to true?

This is easy with a default of false:

{
   "enable_something": "env:SOME_ENV_VAR|b",
   "meddleware": {
      "something": {    
         "enabled": "config:enable_something"
      }
   }
}

But how can it be arranged to default to true? This is potentially a confit/shortstop thing more than a meddleware thing, though I can see changes to any of them to fix it.

Events not fired properly

Is it me or all the middleware:before events are triggered after the middleware is loaded? I can't find a way where the before events are fired before registering, which, from testing, is in line with after events.

Can we support async middleware factories?

Hey,
Assuming this is our config:

...
"my-middleware": {
    "priority": 2,
    "module": {
        "name": "./foo.js",
        "method": "makeMiddleware"
    }
}
...

And this is foo.js:

module.exports = {
  makeMiddleware: function(callback) {
    // init code ...
    callback(function(req, res, next) {
      // whatever
    });
  }
}

As you can see, makeMiddleware is an asynchronous function, which yields to a callback. This can happen because sometime middleware initialization can do async stuff (like reading from a config file, database, etc.). Unfortunately, from looking at the docs and code, it seems that there is no support for async middleware factories, so the code above will fail.

Can we add support for that? ๐Ÿ˜„

Occasional failures in parallel test

I've noticed the following transient failure during a test of the Parallel middleware configuration:

# parallel
ok 100 (unnamed assert)
ok 101 (unnamed assert)
ok 102 (unnamed assert)
ok 103 no response error
ok 104 response is defined
ok 105 response body is defined
ok 106 (unnamed assert)
not ok 107 (unnamed assert)
  ---
    operator: ok
    expected: true
    actual:   false
  ...

Which can be traced to ./test/meddleware.js:505:

        time = Date.now();
        req('/', function () {
            time = Date.now() - time;
            t.ok(time > 1450);
            t.ok(time < 1550); // XXX: not ok 107 (unnamed assert)
            t.end();
        });

Some printf debugging reveals the average time is ~1536 ms [n=200, min: 1515 ms, max: 1557 ms] on my Windows machine (~1518 ms on my Macbook). About 1 in 50 tests fail (2%) on my Windows machine with a time of greater than 1550 ms. Obviously this is jitter in the v8 timers on my work machine, but I've found that bumping the time requirement to 1560 to be sufficient (n=200 runs).

Register middleware based on HTTP method

I know it is possible to register middleware functions based on the route. But how do I register middleware functions based on the same route with different HTTP methods.

For example,
/api GET will have a different set of middleware.
/api POST will have a different set of middleware.

Document session configuration and how to undo keys "higher" up

Kraken by default has an enabled MemoryStore session. I don't want this, but I can't put my session config in json. I need to basically stop Kraken/meddleware from making anything about the session and then app.use at the right time (middleware:after:cookieParser). Right now I'm deleting that with "delete config.get('middleware').session" in onconfig. Whatever the right way is to do something like this, it should be clearly documented.

How to register an property of a module as middleware?

I have a module where it's exported as:

module.exports = {
    middleware : function (req, res, next) { ... }
};

If I use require and register in code, I can do something like:

var middleware = require('someModule').middleware;
app.use('/', middleware);

However, with meddleware, how would I specify it?

"someModule": {
            "enabled": true,
            "priority": 1,
            "module" : {
                "name": "someModule",
                "method": "middleware"
            }
        },

method doesn't work because it expects it to be a constructor function.

Middleware modules are loaded even if disabled

It seems wether a middleware is enabled or not is checked for each request and hence the module must always be loaded from the start. Since I guess most people use this module with krakenjs which comes with a lot of preconfigured modules it would be nice to disable the ones you don't want/need completely without having to fork or assign them to some noop module.

Personally I don't see the need to be able to enable/disable modules at runtime, but maybe there's a special use case that I don't know of. Restarting a node process is not particularly slow/expensive imho and createToggleWrapper itself is somewhat buggy with anonymous functions since it uses eval.

npm5 issue

When installed with NPM5 the "other" user lacks read permissions on this file:
node_modules/meddleware/index.js

This is similar to the issue that was noted for the array-unique module here:
jonschlinkert/array-unique#5

Change config for module definitions.

Currently, middleware modules are specified like this:

{
   "custom": {
        "enabled": true,
        "priority": 30,
        "route": "/foo",
        "module": "./lib/middleware",
        "factoryMethod": "customMiddleware",
        "arguments": [ "foo", { "bar": "baz" } ]
    }
}

This issue proposes to switch to using strings for simple exported middleware, or objects
for including additional information:

{
   "custom": {
        "enabled": true,
        "priority": 30,
        "route": "/foo",
        "module": "./lib/middleware"
    }
}
{
   "custom": {
        "enabled": true,
        "priority": 30,
        "route": "/foo",
        "module":  {
            "name": "./lib/middleware",
            "method": "customMiddleware",
            "arguments": [ "foo", { "bar": "baz" } ]
        }
    }
}

Meddleware 4.0 not working as expected

Has something changed between meddleware 3.0.2 and meddleware 4.0 that would prevent a simple use case from working? I'm reading through the documentation and do not see anything that would indicate that this is the case.

I just spent a good amount of time tracking down the source of a problem that was preventing an application's routes from being loaded via meddleware / express-enrouten.

In my use case, meddleware's app.once('mount', function() { ... }) never ends up firing. Here is an example. Run it with:

$ export NODE_PATH=$NODE_PATH:./lib && node ./bin/index

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.