Fork me on GitHub

diet 0.9

Routing

level:
duration:
30 minutes

Table of Contents

# Intro to Routes

Routing is the process of selecting best paths in a network. Routes are usually symbols to handler functions or middlewares but they can also lead to physical files or errors.

# The Request Response Logic

Request–response or request–reply is one of the basic methods computers use to communicate to each other. When using request–response, the first computer sends a request for some data and the second computer responds to the request.


Usually there is a series of such interchanges until the complete message is sent. Browsing a web page is an example of request–response communication. You can think of request–response as being like a telephone call, where you call someone and they answer the call.


The call initiates a routing process in the server that runs handler functions that has been associated with that route. The handler functions are like valets with specific instructions (functions) assigned to work at specific locations (paths). There job is to move the data in the right direction.


    // When http://localhost/path is requested, respond with "Hello world!"
    app.get('/path', function($){
        $.end('Hello world!')
    })
	

request-response in diet

# The 6 Type of Routes


Path Specific Routes

GET and POST routes are path specific, they can be created by defining a URL pattern and specifying a function that handles incoming requests that match that pattern.


    function handlerFunction($){
    	$.end('Hello world!')
    }
    
    app.get('/pathPattern', handlerFunction)
	


Global Routes

Header, footer, missing and error routes are global. They also have handler functions but we call them middlewares because they are usually called at different stages of the routing process.


Global routes don't need a pattern because they are not triggered by matching URL Paths. Headers and footers are called for every route, missing routes are called when there was no route found and error routes are called when an unexpected error happened in any of the routes.


    function middlewareFunction($){
        $.return() // or $.end()
    }

    app.header(middlewareFunction)
	

# Middleware

The concept of middleware can be confusing sometimes, but don't worry it's actually quite simple. It's just a bunch of functions or even programs linked together. The "middleware layer" is the link itself, the chain that connects functions.

Some real-life examples that are similar in concept to middleware are electric cables in a city, railroads in a country, the plumbing in a house, a bridge between mountains, or a river's stream.

A practical example in our case is image uploading because it involves uploading, cropping and saving data into a database. These operations use 3 distinct applications, formidable, graphicsmagick and mongodb and with routing we can connect them together by registering middleware functions for the route.


    // Register middleware functions for the upload path
    app.post('/upload/picture', upload, crop, save, finish) 
	
    // upload with formidable
    function upload($){
        var form = new formidable.IncomingForm();
        form.parse($.request, function(err, fields, files) {
            $.data.file = files[0]
            $.return();
        });
    }
    
    // crop with graphicsmagick
    function crop($){
        gm($.data.file.source)
        .resize(200, 200)
        .autoOrient()
        .write(writeStream, function (err) {
            if (error) throw error
            $.return()
        });
    }
    
    // save to mongodb
    function save($){
        db.pictures.save($.data.file, function(error, row){
            if (error) throw error
            $.data.file.row = row
            $.return()
        })
    }
    
    // send json response
    function finish($){
        $.success()
    }
    

As you can see the contents of these individual functions actually could be inside a single function too. The reason we separate operations into Objects and Functions is to create modular systems. It is a good practice because smaller chunks of code can be managed more effectively and allows to use other peoples code in ours with much less effort.

# Dynamic Routes


You can also use routing to generate dynamic URLs in your application. If you have 1,000 users you probably don't want to create a file or route for all of them, that's where dynamic routing comes handy.

You can define a pattern such as :username in the route's path and access it with $.params.username as you can see on the example below:


    // Visiting "http://example.com/user/john" should return:
    // "The requested user is john"
    app.get('/user/:username', function($){
        $.end('The requested user is ' + $.params.username) 
    }) 
    

# GET Routes


What is GET?

GET is one of the HTTP methods that describe the way the request should be handled. You can imagine it like a file extension that describes a compression type like PNG or JPEG.



When to use the GET Method?

The GET is used for viewing something, without changing it, while POST is used for changing something. For example, a search page should use GET, while a form that changes your password should use POST.



    // When "http://example.com/search/Batman" is requested
    app.get('/search/:query', function($){
    	// Look for articles in the database with a title "Batman"
        db.articles.find({ title: $.params.query }, function(error, articles){
            // Show errors if there is any
            if(error) throw error      
            
            // Append articles to data object for json response
            $.data.articles = articles 
            
            // Respond with an array of articles
            // { articles: [{ id:1, title: "Batman", content: "hello world!" }] }
            $.json()                   
        })
    }) 
	


Caching GET Responses

The response to a GET request is cacheable if and only if it meets the requirements for HTTP caching described in RFC 2616 Section 13

# POST Routes


What is POST?

POST is one of the HTTP methods that describe the way the request should be handled. You can imagine it like a file extension that describes a compression type like PNG or JPEG.



When to use the POST Method?

The POST is used for any change like creating, updating, saving or deleting something. For example, a search page should use GET, while a form that changes your password should use POST. Since the response of POST requests are not cached by default it's also used for authorization, authentication, payments etc.



    // When "POST http://example.com/settings/changePassword" is requested
    app.post('/settings/changePassword', function($){
    	// Change the session user's password
        db.accounts.update({ 
            "accountId": $.session.id, 
            "password": $.body.newPassword 
        }, function(error, articles){
            if(error) {
            	// Show errors if there is any
            	throw error
            } else {
            	// Respond with a success object: { passed: true }
            	$.succces()  
            }           
        })
    }) 
	


Caching POST Responses

Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource as described in RFC 2616 Section 9.5

# Header Routes


What is a Header Route?

Header routes allow to register callbacks for every route. They are global middlewares. Headers are emitted before any other route and after the signal was constructed. It can be used to prepend informations to the signal like reading cookies and session information from the request headers.




    app.header(function($){
    	$.name = 'john'
    	$.return()
    })
    
    app.get('/', function($){
    	$.end('hello' + $.name) // -> hello john
    })
    
    app.get('/other', function($){
    	$.end('hello other' + $.name) // -> hello other john
    })
	

# Footer Routes


What is a Footer Route?

Footer routes allow to register callbacks for every route. They are global middlewares. Footers are emitted after all the other routes but before response.end was called. It can be used to handle static files etc.



    app.footer(function($){
    	$.name = 'john'
    	$.return()
    })
    
    app.get('/', function($){
    	$.end('hello' + $.name) // -> hello john
    })
    
    app.get('/other', function($){
    	$.end('hello other' + $.name) // -> hello other john
    })
	

# Missing Routes

What is a Missing Route?

Missing routes are called when a request's URL doesn't map to any physical File nor to a Route. You can use this route to create user friendly 404 Not Found pages.


    app.missing(function($){
    	$.end('My Custom 404 Not Found Page')
    })
	


Can I use middlewares?

Yes of course! It works just like any other Route. You can easily attach session data, notifications and anything you want to your missing views.



    // middlewares
    function session($){ /* ... */ }
    
    // Sorry John! The page was not found.
    app.missing(session, function($){
    	$.end('Sorry '+ $.session.name +'! The page was not found.')
    })
	

# Error Routes


What is an Error Route?

An error route is used to catch and handle unexpected errors in your routes.


When to use Error Routes?

When there is an Error in one of the routes or middlewares the response is finished with a default error message including the reason for the error and a stack trace.


While the default error message is useful when you're developing and debugging, it's not a good practice, and not safe to display error messages in production. With the app.error() route you can display a custom user friendly Internal Server Error message.



    app.error(function($){
    	// Throw error message without shutting down the node process
    	console.throw($.fail.error)
    	
    	// Display Error Message to the client: 
    	// 500
    	// Internal Server Error 
    	// Unexpected variable "hello" at index.js:3:4 
    	$.end($.statusCode + '\n' +  $.statusMessage + '\n' +  $.fail.error.message)
    })
	


Can I use middlewares?

Yes you can! It works just like any other Route. You can easily attach session data, notifications and anything you want to your error views.



    // Example Middleware
    function session($){ /* ... */ }
    
    // Display "Sorry John, something went wrong."
    app.error(session, function($){
    	$.end('Sorry ' + $.session.name + ', something went wrong.')
    })
	

# What's next?

Congratulations! Now you know how to route pages, use middlewares and handle errors. It's time to learn to use the signal.