In this blog post, I'll lead you through creating a simple web application. We will be building a web app to help us create and manage a list of tasks. We'll start off with developing the client side (frontend), then transition to the server side (backend). To participate in this tutorial, you need little prior experience with HTML/CSS/JavaScript. I'll try my best to make the content easy to understand, feel free to drop me a line if they're any issues.

Don't count on it, but if time permits (technically if I'm not too lazy), I'll do another post where we translate the web app we built into a mobile app.

Coming up with an app to build was quite challenging for me. My goal was to pick something super simple that makes it welcoming for a beginner. But at the same time, I needed something that will cover a lot of important topics like databases, client-server interaction, ajax, and responsive web app development.

Getting Started

All the code can be found on my github account.

Tools to partake in this tutorial, you need

  1. Computer/Machine - Technically, we can build this web app on a smartphone, but I wouldn't do that. This tutorial will require that we download a couple of tools, these tools are not available on a phone. Hence, you need a computer running any operating system (Windows, Linux, Mac OS x), and has a modern operating system.
  2. Text editor - Any program you can use to edit text is sufficient. But for programming, I suggest getting something that has syntax highlighting. Here are a couple of suggestions, Visual Studio Code, Notepad++, SublimeText 2/3, Vim (UNIX/Linux/mac), Visual Studio.
  3. node.js - This is the web server that we'll use to serve up our web page. They're other web servers like IIS, Apache, Nginx, etc. But I go with node because we wouldn't have to go learn a new language and also because it's like a big deal these days. Go ahead, visit node.js and set it up for your machine.
  4. Data store - Since we're building a dynamic web app, we need a means to store the data provided by the user. For simplicity sake, I chose to use a no-sql solution. In particular, I chose Azure Table Storage. You can choose any data store solution you want.

NOTE: Since we want to build and test this app locally, we need the data store to be able to run locally on our local machine. Azure Table Storage only has a local emulator for Windows only (at least at the moment of this post). For non-windows users, you can still follow the tutorial, you just need to find an alternative. A popular alternative is mongodb.com, the only thing is that the code to store data might be a bit different.

Frontend JavaScript

In this section, we go over all the frontend JavaScript code that controls the web app. For each JavaScript file, I'll provide the link to the code, then elaborate on some of the important components. You can follow the link to the source code to see the full code.

All the code in this web app, I use 2 (JavaScript) programming styles. They are, Immediately Invoked Function Expression(IIFE) (function() { })(), and Strict mode 'use strict'. In a nutshell, these 2 styles encourages me to avoid certain unsafe practices while programming in JavaScript. (Those benefits are above the scope of these tutorials).

To make life easy for us, I've gone ahead to use jQuery. jQuery, is a DOM manipulation JavaScript library. We use the dollar sign $ to access jQuery in our code. For example, $(FUNCTION_TO_EXECUTE) tells the browser that when the DOM is ready, execute the function.

We can also use jQuery to find and manage an HTML element. Here $("#ELEMENT_ID").submit(function(event) {}), I tell jQuery to find an element with the ID ELEMENT_ID, then attach a function/callback to execute when the submit event is raised for that element. In short, we're setting up an event listener for that element. Feel free to go to jquery.com to learn more about jQuery.

JavaScript files:

script.js - source code

This code file contains the main code that orchestrate the web app. This code file loads all the users tasks on page load, it displays UI for the user to use in managing tasks.

    (function(window, $, tasks) {
        'use strict';

        // tasks -> object that contains code for managing tasks

        // Store the task that is currently being worked on
        var activeTask = {};

        function loadTaks() {
            // use the appropriate method in tasks to load all the tasks
        }

        function createNewTask(taskTitle_Name) {
            // Use the appropriate method in tasks to create a new task
        }

        function generateTaskHTML(taskTitle_Name, id, action) {
            // Generates the HTML code for displaying a single task
        }

        function displayErrorDialog(action, reason) {
            // Generates and displays an error message HTML
        }

        function taskClicked() {
            // Called when any given task is clicked
            // Here, we figure out the active task,
            // Then setup the data for the modal
            // Finally, display the modal
        }

        // Use JQuery to tell the browser to execute some code when the DOM is ready
        $(function() {

            // Load all the tasks
            loadTaks();

            $("#createTaskForm").submit(function(event) {
                // Code to execute when the create task form is submitted

                // The line below prevents the browser from trying to submit that form
                // We do this since we'll be handling the form data on the browser.
                // By the default, the browser tries sending the data to the server for processing.
                event.preventDefault();
            });

            $('#modalForm').submit(function(event) {
                // The line below prevents the browser from trying to submit that form
                // We do this since we'll be handling the form data on the browser.
                // By the default, the browser tries sending the data to the server for processing.
                event.preventDefault();
            });

            $('#modalDelete').click(function() {
                // Code to execute when the delete button in the modal is clicked
            });

            $('#modalSaveChanges').click(function() {
                // Code to execute when the save button in the modal is clicked
            });
        });
    })(window, $, window.todo.tasks);

task.js - source code

This file contains code that assists with all the CRUD (Create, Read, Update, and Delete) actions on the tasks. These CRUD actions entails updating the local cache and the data in the backend. To make requests to the backend, we rely on code from ajax.js.

The methods exposed by this code files all have the given signature. pram1, param2, ..., successCallback, errorCallback. successCallback is called when everything goes well, errorCallback is called when something goes wrong.

    (function(window, $, ajaxHelper) {
        'use strict'; 

        // ajaxHelper -> Handles making request to the backend


        // cachedTasks -> Used to store the tasks locally on the browser
        // taskListID -> Stores the task list ID extracted from the URL
        var cachedTasks = {},
            taskListID = location.pathname.substring(1);

        // In other to use the functionality of the codes in this file, 
        // I've got to make a variable/object available so another code can use it.
        // I'm exposing this code via the an object with the name [todo].
        // [todo] is a name created by me, feel free to call it anything.
        window.todo = window.todo || {};

        // create a task, calls the appropriate method in the ajaxHelper object
        function createTask(title, successCallback, errorCallback) {}

        // get a task, calls the appropriate method in the ajaxHelper object
        function getTask(id) {}

        // get all the tasks for the current taskListID, calls the appropriate method in the ajaxHelper object
        function getAllTasks(successCallback, errorCallback) {}

        // update a task, calls the appropriate method in the ajaxHelper object
        function updateTask(id, title, status, note, successCallback, errorCallback) {}

        // delete a task, calls the appropriate method in the ajaxHelper object
        function deleteTask(id, successCallback, errorCallback) {}

        // export the CRUD methods via the [todo] object
        window.todo.tasks = {
            createTask: createTask,
            getTask: getTask,
            getAllTasks: getAllTasks,
            updateTask: updateTask,
            deleteTask: deleteTask
        };

    })(window, $, window.todo.ajaxHelper);

ajax.js - source code

This file contains code that assists with all the AJAX operations. I use the $.ajax, and $.get from jQuery to make the ajax request.

    (function(window, $) {
        'use strict';

        var index = 0;
        window.todo = window.todo || {};

        function performPOST(url, data, successCallback, errorCallback) {
            // helper method to perform AJAX POST request
            // I introduced this method to avoid repeating code.
        }

        function createTask(taskListID, title, status, note, success, error) {
            // sends a request to the server, instructing it to create a task within a task list.
        }

        function getTask(taskListID, taskID, successCallback, errorCallback) {
            // sends a request to the server, instructing it to get a task within a task list.
        }

        function getTasks(taskListID, successCallback, errorCallback) {
            // sends a request to the server, instructing it to get all tasks within a task list.
        }

        function updateTask(taskListID, id, modifiedTask, success, error) {
            // sends a request to the server, instructing it to update a task within a task list.
        }

        function deleteTask(taskListID, id, success, error) {
            // sends a request to the server, instructing it to delete a task within a task list.
        }

        // export the ajax helper codes
        window.todo.ajaxHelper = {
            createTask: createTask,
            getTask: getTask,
            getTasks: getTasks,
            updateTask: updateTask,
            deleteTask: deleteTask
        };
    })(window, window.$);

Backend JavaScript [Node.js]

In this section, we go over all the backend JavaScript code that makes up the core of the web app. The backend is built using Node.js, due to the raw nature of node.js, I'm using Expressjs.com to handle the routing for the web app.

JavaScript files:

app.js - source code

This is the main portion of the web app. It handles incoming request, dispatches calls to perform specific CRUD operations, and many more cool stuff.

        // express.js
        var express = require('express');
        // azure storage node.js sdk
        var azure = require('azure-storage');
        // module for validating data
        var validator = require('validator');
        // express.js middle-ware for parsing the body of a request
        var bodyParser = require('body-parser');
        // the tasks module
        var tasks = require('./tasks');

        var appName = "Simple To-Do List app";
        var app = express();
        var azureTableService = azure.createTableService("UseDevelopmentStorage=true");

        // Middle-ware for validating a task list ID
        var taskListIDChecker = function(req, res, next) {
            var taskListID = req.params.taskListID;

            if (!taskListID || !taskListID.match(/^[a-zA-Z0-9]{8}$/)) {
                console.error('Invalid task list - ' + taskListID);
                res.status(500).send('Invalid task list - ' + taskListID);
            } else {
                next();
            }
        };

        // Middle-ware for validating a task ID
        var taskIDChecker = function(req, res, next) {
            var taskID = req.params.taskID;

            if (!taskID || !validator.isUUID(taskID)) {
                console.error('Invalid task id - ' + taskID);
                res.status(500).send('Invalid task ID - ' + taskID);
            } else {
                next();
            }
        };

        // (longjohn) is used for dumping better stack trace when an error occurs
        if (process.env.NODE_ENV !== 'production') {
            require('longjohn');
        }

        // When we app is started try creating the table if it doesn't exist already
        // After, call the callback passed to it
        azureTableService.createTableIfNotExists(tasks.tableName, function(error, result, response) {
            // If everything goes well
            if (!error) {

                // Call the init method in the tasks module (see tasks.js)
                tasks.init(azureTableService,
                    azure.TableUtilities.entityGenerator,
                    azure.TableQuery,
                    azure.TableBatch);

                // Setup the routes

                app.get('/', function(req, res) {
                    // Create a new task list
                    tasks.createTaskList(function(taskListID) {
                        // On success, redirect to the new task list
                        res.redirect(301, '/' + taskListID);
                    }, function(err) {
                        // ERROR
                        res.status(500).send(err);
                    });
                });

                app.post('/create/:taskListID', taskListIDChecker, function(req, res) {}
                    // Create a new task in a task list
                });

                app.get('/task/:taskListID/:taskID',
                    taskListIDChecker,
                    taskIDChecker,
                    function(req, res) {
                        // Get a task from the given task list
                    }
                );

                app.get('/tasks/:taskListID', taskListIDChecker, function(req, res) {
                    // Get all the tasks in a given task list
                });

                app.post('/update/:taskListID/:taskID',
                    taskListIDChecker,
                    taskIDChecker,
                    function(req, res) {
                        // Update a task in a specific task list
                    }
                );

                app.post('/delete/:taskListID/:taskID',
                    taskListIDChecker,
                    taskIDChecker,
                    function(req, res) {
                        // Delete a task from a given task list
                    }
                );

                app.get('/:taskListID', taskListIDChecker, function(req, res) {
                    // Returns an HTML file that will use ajax to load all the tasks
                });

                app.use(express.static('public'));
            } else {
                // When an error occurs
                app.get('/', function(req, res) {
                    res.send('<h1><i>' + appName + '</i> not available at the moment, please try again later.</h1>');
                });
            }
        });

tasks.js - source code

This source code file contains the main code for interacting with the database. This code is then exported as a module to make it accessible in the other part of the backend code.

        function createTaskRecord(taskListID, rowKey_TaskID, title, status, note) {
            // Use method to create a task to be stored in the DB
        }

        function cleanUpTask(taskFromTable) {
            // Use method to trim a task before sent to the client
        }

        function stringify(str, trim) {
            // Safer method to turn an object to string
        }

        function createTaskList(successCallback, errorCallback) {
            // Use this method to create a task list
        }

        function createTask(taskListID, title, status, note, successCallback, errorCallback) {
            // Use this method to create a task
        }

        function createTasks(taskListID, tasks, successCallback, errorCallback) {
            // Use this method to create a bunch of tasks
        }

        function getTask(taskListID, id, successCallback, errorCallback) {
            // Use this method to get a specific task from a task list
        }

        function getTasks(taskListID, successCallback, errorCallback) {
            // Use this method to get all the tasks for a given task list
        }

        function updateTask(taskListID, id, updatedTask, successCallback, errorCallback) {
            // Use this method to update a task
        }

        function deleteTask(taskListID, id, successCallback, errorCallback) {
            // Use this method to delete a task
        }

        function init(tableService, entityGenerator, tableQuery, tableBatch) {
            // This is used to initialize this task module
        }

        // Export this module
        exports = module.exports = {
            createTask: createTask,
            getTask: getTask,
            getTasks: getTasks,
            updateTask: updateTask,
            deleteTask: deleteTask,
            createTaskList: createTaskList,
            init: init,
            tableName: TABLE_NAME
        };

Thanks for reading, hope you've not passed out reading the lengthy post.