Fork me on GitHub

diet 0.9

Signal

level:
duration:
30 minutes

Table of Contents

# Intro

Node.js by default has two arguments request and response when creating a HTTP(s) server using http.createServer(). Diet combines these two objects into a single Signal Object that is represented by a dollar sign $.


While the signal combines these two objects, we are still using the request-reponse logic. This simplification allows faster learning, coding and an easier to use middleware layer.

# What are Request Methods?

We use request methods to identify resources on the server so that we can read, edit, save or delete them. We use them a lot since almost all dynamic interactions involve some kind of identification.

# Query String

The query string is part of the URL containing data that does not fit conveniently into a hierarchical path structure (/users/john/pictures). Diet parses the query string into a JSON object called $.query. The query string starts with ? and the keys are separated by &.


Query String Structure

    # An example URL with a query string having 2 key-values
   
    # --------------- ↓ the query string starts with a ?
    "http://localhost/?search=cats&show=pictures"
    # ---------------------------- ↑ key-values are separated by a &
	

    # The above URL's query string is parsed into $.query
    $.query = { 
       search: "cats", 
       show: "pictures" 
    }
	


Query String Usage Example

Let's say a user requests the URL "http://localhost/user?id=40". The query string in this URL is ?id=40 which is converted into { id: 40 }. We can use the value to find a User's Account with the ID 40 in the database as you can see on the example below.


    // When http://localhost/user?id=40 is requested
    // respond with "{ account: { id: 40, name: 'John Doe', age: 22 } }"
    app.get('/user', function($){
                            // ↓ $.query.id is parsed from the url "?id=40"
    	db.accounts.find({ id: $.query.id }, function(error, account){         
    	    if(error) throw error
    	    $.data.account = account
    	    $.success()
    	})
    })
	

# Query Parameters

The query parameters are used to create semantic URL's. Diet parses the query parameters into a JSON object called $.params.


Query Parameter Structure

    # An example pattern having 2 parameters
    app.get('/users/:id/:page', handlerFunction)
	

    # The URL below is catched by the above pattern 
    
    # ---------------------- ↓ :id
    "http://localhost/users/40/videos"
    # ---------------------------- ↑ :page
	

    # The above URL's query parameters are parsed into $.params
    $.params = { 
        user: 40, 
        page: "videos" 
    }
	


Query Parameter Usage Example

Let's say a user requests the URL "http://localhost/user/40". The query parameter in this URL is 40 which is catched by a pattern such as :id, and it's converted into { id: 40 }. We can use the value to find a User's Account with the ID 40 in the database as you can see on the example below.


    // When http://localhost/user/40 is requested
    // respond with "{ account: { id: 40, name: 'John Doe', age: 22 } }"
    app.get('/user/:id', function($){
                            // ↓ $.params.id is parsed from the url ":id"
    	db.accounts.find({ id: $.params.id }, function(error, account){         
    	    if(error) throw error
    	    $.data.account = account
    	    $.success()
    	})
    })
	

# Headers

Just like HTML documents have head tags, HTTP Requests and Responses have them too, but with a different syntax. HTTP Headers hold informations about the client, the server or the request/response body. Such informations are Cookies, Cache-Control, Content-Type and many others. Both the request and response have HTTP headers.



Reading Request and Response Headers

You can read headers with the $.header() function.


    // When http://localhost/myUserAgent is requested respond with:
    // Your agent is: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0
    app.get('/myUserAgent', function($){
        var agent = $.header('User-Agent')
        $.end('Your agent is: ' + agent)
    })
	


Sending Response Headers

You can set headers also with the $.header() function by using the second argument as the new value. Please note that you can set Request fields.


    // When http://localhost/contentLength is requested respond with:
    // with the header: Content-Length=34
    // and with the body: The length of this paragraph is 34
    app.get('/contentLength', function($){
        var response = 'The length of this paragraph is 34'
        $.header('Content-Length', response.length)
        $.end(response)
    })
	

# Body

Just like HTML documents have body tags, HTTP Requests and Responses have them too, but with a different syntax. Every response has a body but only POST requests have it. Please note I'm highlighting response and request for a reason, it's easy to get confused, keep in mind they are not the same!



Reading POST Request Body with $.body

Let's say this HTML form is submitted with the values: username: "john" and password "1234":


    <form method="POST" action="/login" >
    	<input type="text" name="username" value="john" />
    	<input type="password" name="password" value="1234" />
    	<input type="submit" />
    </form>
	

All the inputs with a name and value will be converted into a JSON object called $.body. The above form when submitted will create a request that diet can convert into:


    $.body = {
        username: "john",
        password: 1234
    }
	

In your routes you can work use the keys and values of $.body for many things. In this case checking the database records to perform the login would make sense, but for the sake of simplicity, we'll just output the values back to the client in JSON format.


    // When http://localhost/login is requested, respond with:
    // { username: "john", password: 1234  }
    app.post('/login', function($){
        $.data.username = $.body.username  // Add body username to JSON data
        $.data.password = $.body.password  // Add body password to JSON data
        $.json()                           // Send JSON response
    })
	

# Responding

In all of our previous examples we responded in our Routes because every request needs a response. When there is no response the client will probably show a Connection Timeout message to the users. There are two ways to respond, sending/streaming and ending the response.

$.end

Ending a response is quite easy, you just need to call the $.end function. Every request ends with $.end, even when using $.json, $.success and the $.error methods.


    // this will show a blank page in a browser 
    app.get('/', function($){
        $.end()
    })
	


$.send

You can send the response body as a whole with $.end() or you can stream it in chunks with $.send(). The example below demonstrates this:


    app.get('/', function($){
    	$.send('hello')        // send "hello" immediately
    	setTimeout(function(){
    	    $.send('world')    // send "world" after 2 seconds
    	    $.end()            // end after 2 seconds
    	}, 2000)
    })
	

# Responding with JSON

The signal has some special methods that are shortcuts for JSON stringified response data. In some cases defines some default values and headers too.

JSON Response

Using the $.json() function we can end the answer with the JSON stringified version of $.data.


    // When http://localhost/ is requested, respond with:
    // { pancakes: "are good" }
    app.get('/', function($){
    	$.data.pancakes = 'are good'
        $.json()
    })
	

# JSON Error Handling

Using the $.success, $.failure, $.error and $.passed methods we can handle errors for clients expecting a JSON response.


    app.post('/login', function($){
    	// check if username and password is not empty
        if(!$.body.username) $.error('username', 'empty')
        if(!$.body.password) $.error('password', ' empty')
        
        // if any of the above tests fail $.passed is set to false
        // since calling $.error also sets $.passed to false 
        if($.passed){
            // ... do custom login operation ...
            
            // then return: 
            // { passed: true }
            $.success()
        } else {
            // return:
            // {   
            //     passed: false, 
            //     errors: { 
            //          username: 'empty', 
            //          password: 'empty' 
            //     } 
            // }
            $.failure()
        }
    })
	

# Passing data between middleware functions

When adding methods to the signal each following middleware function will have access to the new methods too. This allows to transmit data, functions and classes between functions.


    app.header(function($){
        $.myVar = 'hello'
        $.return()
    })
	
    function middle($){
    	$.myVar += 'world'
    	$.return()
    }
    
    function middle2($){
    	$.myVar += '!'
    	$.return()
    }
    
    function finish($){
    	$.end($.myVar) // -> hello world!
    }
    
    app.get('/', middle, middle2, finish)
	

# What's next?

Congratulations! Now that you know how to use the signal, you can already do anything! Now the only thing left to learn is Modules which will make your life a lot easier.