clever-controller

Lightning-fast flexible controller prototype

Clever Controller

NodeJS Controller for the CleverStack ecosystem, designed to work with or without CleverStack.

It is published in npm so a simple npm install clever-controller will suffice.

npm i clever-controller

The main aim of the controller is to help simplify the most common tasks that you need to do when setting up routes and functions/classes to handle them.

--

Controllers are the thing that directly responds to each HTTP Request that comes into your application, as such each web request will result in (if routed) a new instance of a Controller (Class).

Avoid callback soup/hell and promise slowdown by using this.proxy() or this.callback() in the Static section of the Controller, this keeps code nesting to a minimum and brings code re-use up.

// Example, taken from the RoleController inside the clever-roles module 
RoleService
    .list()
    .then( this.proxy( 'handleServiceMessage' ) )
    .catch( this.proxy( 'handleException' ) );
 
// You can also do 
this.hello = 'YEAH!'; // Only here for demo 
RoleService
    .list()
    .then( this.proxy( function( roles ) {
        console.log( this.hello ); // Will output 'YEAH!' 
    }))
    .catch( this.proxy( 'handleException' ) );

By taking advantage of V8's Hidden Classes, Inline Caches and the Optimizing Compiler (which generates effecient machine code to use in-place of your javascript) your application is blisteringly fast AND easy to use.

--

  • Lightning fast
    • V8's Hidden Classes & Inline Caches
    • V8's Optimizing Compiler which generates efficient machine code to run in place of your javascript
    • Clean, Efficient and Optimized code
  • Super methods
    • Static inheritance
    • Prototypal inheritance
  • Setup and initialization methods for both Static and Prototype
  • Easy-to-use method proxys with currying ( Static.callback() and Prototype.proxy() )
  • Internet Addressable functions, simply add 'Action' to the end of your controllers method() name and it will be Internet Addressable (as long as a route points to that controller).
  • Private functions, any function without 'Action' on the end of its name is NOT Internet Addressable
  • Easy accessors for Request and Response, available as this.req and this.res.

--

  1. Automatic Routing ( Static.autoRouting )
    • Ability to turn on or off autoRouting (true or array for ON, false for OFF)
    • Array notation allows (Connect or any other) Middleware to be attached to the routes that are autoRouted

here are some examples:

{
    // ... 
 
    autoRouting: [
        // As a String (only available for methods on the controllers Static, either inherited or defined) 
        'middlewareFromThisController',
 
        // Any function 
        function definedFunction( reqresnext ) {
            // any function (from any file) that takes these three arguments can be attached 
        },
 
        // CleverStack example 
        PermissionController.requiresPermission({
            all: 'Example.$action', // optional: ability to define rules based on action (Example.post for POST, etc) 
            postAction: false       // optional: ability to define overrides per action (Overrides must include 'Action') 
        })
    ],
 
    middlewareFromThisControllerfunction( reqresnext ) {
        // Attached inside of autoRouting array 
        next();
    }
 
    // ... 
}
  1. Route name ( used with autoRouting )

    • Ability to either define your own route (name), or have it automatically generated based on the filename of your controller! (ExampleController would have a route name of /user and /users )
  2. Manual Routing

You can define your own routes like this

// Default route setup ~ '/example' or '/example/' or '/example/hello' 
app.all('/example/?:action?', ExampleController.attach())
 
// Action + ID Routes setup ~ '/example/custom/12' 
app.all('/example/:action/:id?', ExampleController.attach())
 
// A common use/place for middleware in a controller 
app.use(ExampleController.someMiddleware);

Note: if you use both types of routes, be sure to place your routes in this order

We use Express' routing, so be sure to check it out at http://expressjs.com/api.html#app.VERB

Using Controller is easy but if you have any problems (or questions) please raise an issue on github

module.exports = ExampleController = function() {
    return Controller.extend(
    {
        // example that returns JSON, available from route '/example/hello' 
        helloActionfunction() {
            this.send({
                message: 'Hi there'
            })
        },
 
        // example that renders a view, available from route '/example/view' 
        viewActionfunction() {
            this.render('view.ejs', {});
        }
    });
};

--

module.exports = ExampleController = function() {
    return Controller.extend(
    {
        someMiddlewarefunction(reqresnext) {
            res.send({
                message: 'Hi from middleware!'
            });
        }
    },
    {
        // example that returns JSON, available from route '/example/hello' 
        helloActionfunction() {
            this.send({
                message: 'Hi there'
            })
        }
    });
};

--

module.exports = ExampleController = function() {
    return Controller.extend(
    {
        postActionfunction() {
            this.send({
                status: 'Created record!' 
            });
        },
 
        listActionfunction() {
            this.send({
                status: 'Sending you the list of examples.'
            });
        },
 
        getActionfunction() {
            this.send({
                status: 'sending you record with id of ' + this.req.params.id
            });
        },
 
        putActionfunction() {
            this.send({
                status: 'updated record with id ' + this.req.params.id
            });
        },
 
        deleteActionfunction() {
            this.send({
                status: 'deleted record with id ' + this.req.params.id
            });
        }
    });
};

--

When doing a GET /example it will route to either listAction first OR getAction if listAction is not defined.

If neither are defined, express's .next() function will be called allowing it to fall through the controller and move onto any other middleware you may have configured.

If you want /example/hello as a route, you can simply implement helloAction in your controller and it will be automatically routed to it.

This is the default way to setup a controller to use actions. By default you can also visit /example/12 and it will route to the getAction function in your controller (if it's defined) with this.req.params.id set for you to use (the same applies for all HTTP methods, eg PUT/DELETE/POST/et cetera)

node test/performance/master.js
 
clever-controller: 1 server, 1 client processes: avg 2742 req/second (2726, 2758)
clever-controller: 2 server, 1 client processes: avg 2764 req/second (2784, 2745)
clever-controller: 3 server, 1 client processes: avg 2669 req/second (2671, 2668)
clever-controller: 4 server, 1 client processes: avg 2676 req/second (2688, 2664)
clever-controller: 5 server, 1 client processes: avg 2715 req/second (2773, 2661) *
raw express.js:    1 server, 1 client processes: avg 2766 req/second (2743, 2790)
raw express.js:    2 server, 1 client processes: avg 2793 req/second (2780, 2807)
raw express.js:    3 server, 1 client processes: avg 2730 req/second (2743, 2717)
raw express.js:    4 server, 1 client processes: avg 2710 req/second (2722, 2699)
raw express.js:    5 server, 1 client processes: avg 2635 req/second (2712, 2562) *
npm test