Express Baby Steps

Beginners to Node.js and especially express.js will start out grouping all their functionality into one file.

var express = require( 'express' )  
var app = express()

app.get( '/', function( req, res ){  
    res.send( 'This is the home page' )
})

app.get( '/greet/:name', function( req, res ){  
    res.send( 'Hello ' + req.params.name )
})

This works for small projects, but once you start including business logic and database work the file can quickly scale to hundreds or even thousands of lines of code.

In addition to being messy, it becomes increasingly easy to forget where you are and what you're doing. Bugs slip through un-noticed, modular functionality becomes spread accross 300 lines, and the whole project begins to spiral.

To get around this problem, you need to keep in mind that all app.get really asks for is a path to match and a function to handle the request. It doesn't matter if you pass an anonymous function, a pre-defined function, or run a custom function that returns a function.

// All valid

// Directly pass an anonymous function handler
app.get( '/method-one', function( req, res ){  
    res.send( 'hello from method 1' )
})

// Pass a pre-defined function
function methodTwoHandler( req, res ){  
    res.send( 'hello from method 2' )
}
app.get( '/method-two', methodTwoHandler )

//Build a request handler dymamically
function makeHandler( message ){  
  return function( req, res ){
    res.send( message )
  }
}
app.get( '/method-three', makeHandler( 'Hello from method 3!' ) )  

For most small to medium projects, the router-controller configuration should be adequate.

project/  
  | index.js
  | router.js
  | controller.js

This setup enforces an extremely important programming concept, seperation of concerns. In this example, each file is responsible for one thing and nothing more. Equally important as defining what a code module does do is defining what that module does not do.

The index file does run the http server according to the configuration.
It does not set up routes or respond to routes.

// index.js
var express = require( 'express' )  
var app = express()

var router = require( './router' )  
app.use( router )

app.set( 'port', process.env.PORT || 8888 )

app.listen( app.get( 'port' ), function(){  
  console.log( 'Application listening on port ' + app.get( 'port' ) )
})

The Router does map URLs to the functions responsible for handling them.
It does not create any handling functions by itself.

// router.js
var router = require( 'express' ).Router()  
var controller = require( './controller' )

router.get( '/', controller.homePage )  
router.get( '/login', controller.login )  
router.post( '/login', controller.handleLogin )  
router.get( '/about-us', controller.render( 'about-us' ) )  
router.get( '/contact-us', controller.render( 'contact-us' ) )

module.exports = router  

The Controller does provide functions to handle requests.
The Controller does not care which urls it is mapped to.

// controller.js

var database = require( 'my-database-module' )

module.exports = {  
    homePage: function( req, res ){
        res.render( 'home-page' )
    },
    login: function( req, res ){
        res.render( 'login' )
    },
    handleLogin: function( req, res ){
        database.logIn()
        res.redirect( '/' )
    },
    render: function( page ){
        return function( req, res ){
            res.send( page )
        }
    }
}

If each of your files does one and only one thing, it becomes much easier keep your code straight in your head, as well as find where bugs are hiding.