Create a Simple ToDo App with Expressjs

Hey, folks..!! Time to do some Express…!!

This is a beginner’s tutorial where we’ll be learning the how to install express, displaying a simple helloworld and finally by creating a simple ToDo App using Express js.

If you are looking for the complete code, a link to the GitHub repo is provided at the end.

So, what’s Express.js?

Express is a simple and minimal framework for web applications that are built on Node.js. Though a little unheeded among other competitive frameworks, it’s minimalism and JavaScript extending capabilities makes it the framework for popular blogging platforms like Ghost and prominent websites like GeekList, MySpace etc.

Express – Fast, unopinionated, minimalist web framework for Node.js

Express framework when used for building network applications becomes a lot easier as it helps the developer as it needn’t be written from scratch. Get to know more about Express here: Read more

Getting Started

Install Nodejs before proceeding to the next steps.

Installing Express.js

To kick start our learning, begin by creating a new directory to contain our application.

mkdir express-todo
cd express-todo

Once inside the directory create the package.json file for our application.

npm init

Provide the required information. In our case, we will leave them as default.
Now let’s actually install Express.

npm install express --save

The installation process is quite tricky and errant, If everything went fine there will be a new directory called node_modules in our project directory. This is where npm (Node Package Manager) stores all the dependencies for our project.

Hello Express!

Now that we’ve got everything installed, let’s run a classic hello world example to see if everything is working.

For that create a new file, let’s call it helloworld.js.

touch helloworld.js

Open it using your favorite text editor (Atom :heart:) and add the following code to it.

const express = require('express');
const app     = express();

// define the routes
app.get('/',(req,res) => { // get root
  console.log("GET request recieved");
  res.send('Hello Express!');
});

// listen for requests on port 3000
app.listen(3000,() => {
  console.log("Hello Express running on port 3000!");
});

Save and Run the created helloworld.js file by the following command:

# you must be inside your project directory
node helloworld.js

Go to http://localhost:3000  on your browser. You should see the message Hello Express! there.

# you should see something like this on your terminal
Hello Express running on port 3000!
GET request received

Awesome! You just built your first Express application.
Now let’s build something more sophisticated.

The To-Do App

Before we jump in and write any code, let’s take a minute and make a clear picture of what we are building. The two main concepts required to make the app functional are:

  • Store:

    A place to store our to-dos and other information related to it. We can use a database system for this purpose.

  • View:

    An interface for the user to play around with the to-dos. We can use a template engine to build our view.

Fortunately, Express makes it very easy to integrate everything.

The store

As mentioned above we need a store to keep all our data. Here, we will be using MongoDB to set up our store. There are plenty of other options like MariaDB, sqlite3 etc.

For convenience, let’s use a cloud hosted version of MongoDB instead of installing it on our machine. MongoDB has their own Database as a Service(DaaS) platform called Atlas. So sign-up (they have got a free plan) for an account and create a new cluster for our project. Let’s call it todo-cluster. Also, provide a username and password which we will use later to connect to the database from Express.

Hit Confirm and Deploy. It’ll take a few minutes.

Once that is complete, click on connect and grab the URI Connection String. Also, set the IP whitelist to ACCESS FROM ANYWHERE(not recommended in a production environment).

atlas-mongodb-cluster

For the Express to access it, we need to install MongoDB. In order to install, type in the following commands:

npm install mongodb --save

Let’s call the database todo-store (we need this later).

Template Engine

To build our view we need a template engine. You got plenty of options here too but we will stick with Handlebars. The syntax is very simple and intuitive but flexible enough for our little to-do app.

Since Express doesn’t come with Handlebars out-of-the-box we need to install it. So let’s do another npm install.

npm install express-handlebars --save

Once that is installed, create a directory to contain our views.

mkdir views

And another directory for our layouts(just to stay organized).

mkdir views/layouts

Great Job!

Now with all of that out of the way, let’s get back to coding.

Building the App

It is time to put everything together with code. So create a new file named app.js in your project directory.

First, we need to import our dependencies.

const express = require('express');
const mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var exphbs  = require('express-handlebars');

Now let’s setup our database connection.

// replace it with your URI Connection String
const url = 'mongodb://todo-db:@mycluster0-shard-00-00-wpeiv.mongodb.net:27017,mycluster0-shard-00-01-wpeiv.mongodb.net:27017,mycluster0-shard-00-02-wpeiv.mongodb.net:27017/?ssl=true&replicaSet=Mycluster0-shard-0&authSource=admin';

// initial setup
MongoClient.connect(url,(err,db) => { // connect to the database
  if (err) throw err;

  db.createCollection("todos",(err,res) => { // create a new collection called todos
    if (err) throw err;
    console.log("todos collection created");
    db.close();
  });
});

// some helper functions

function insertTodo(obj,callback) { // insert a todo into the database
  MongoClient.connect(url,(err,db) => {
    if (err) throw err;

    db.collection("todos").insertOne(obj,(err,res) => { // insert obj into todos collection
      if (err) throw err;
      console.log("Todo inserted!\n",obj);
      db.close();
    });
  });
  callback();
}

function deleteTodo(id,callback) { // delete a todo by id
  MongoClient.connect(url,(err,db) => {
    if (err) throw err;

    const query = { _id: new mongodb.ObjectId(id) }; // create a query to identify the item to be deleted

    console.log("Query: ",query);

    db.collection("todos").deleteOne(query,(err,obj) => { // delete the item
      if (err) throw err;

      console.log(obj.result.n + " document(s) deleted!");
      db.close();
    });

  });
  callback();
}

function getTodos(callback){ // get todos from the database
  MongoClient.connect(url,(err,db) => {
    if (err) throw err;

    db.collection("todos").find({}).toArray((err,res) => { // retrieve all the todos
      if (err) throw err;
      console.log("retrieved " + res.length + " todos");
      callback(res); // pass it to the callback function after retrieval
      db.close();
    });
  });
}

Now let’s set-up the template engine.

var app = express(); // create an express application

// set handlebars as the template engine
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');

Next part on the go is building our view.

Let’s start with our main layout. The {{{body}}} part will be replaced by other views based on our code.

<!-- views/layouts/main.handlebars -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>To-Do App</title>
</head>
<body>

    {{{body}}}

</body>
</html>

Now let’s build the interface.

<!-- views/home.handlebars -->
<h1>
  To-Do App
</h1>
<ul>
  {{#if empty}}
    No Todos found.
  {{/if}}
  {{#each todos}}
  <li>
    <p>{{title}}</p>
    <button class="delete-btn" type="button" data-id="{{_id}}">x</button>
  </li>
  {{/each}}
</ul>

<form action="/add" method="post">
  <input type="text" name="title" placeholder="todo">
  <input type="submit" name="add" value="Add Todo">
</form>

<script>
  function deleteTodo(id) {
    // place a requests to delete todo with id
    console.log("Deleting todo ",id);

    // prepare the request
    const req = new Request('/delete',{
      headers: new Headers({
        'Content-Type': 'application/json',
      })
    });

    // send the request
    fetch(req,{
      method: 'POST',
      body: JSON.stringify({
        "id": id, // id of the todo to be deleted
      })
    }).then(function(res) {
      setTimeout(() => {location.reload();},600); // reload the page after request
    });
  }

  var btns = document.getElementsByClassName("delete-btn");
  Array.from(btns).forEach((element) => { // attach onclick listeners to all the todo delete buttons
    element.addEventListener('click',(e) => {
      console.log("Clicked!",e.target.getAttribute("data-id"));
      deleteTodo(e.target.getAttribute("data-id"));
    });
  });
</script>

The interface code is pretty much self-explanatory. It simply lists the todos fetched from the database, each with a delete button having an event listener attached to them, and also provides a form for adding new todos.

Note: It contains ES6 code. So transpile it before deployment to get maximum browser compatibility.

Before moving onto routing, we need one more dependency to help handle JSON responses from forms. The response in the todo note, i.e, each entry in the form is handled in the form of JSON.

npm install body-parser --save

Body Parser is a middleware that helps parse incoming request bodies. Inform express to use it.

const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

Let’s define our routes.

We have three different functions here,

  • List To-Do
  • Add To-Do
  • Delete To-Do

For ease of use, we have already built a common interface for all these functionalities.

So how do we link them together?

Since the List function is always required as the user need to see his/her To-Dos we can define that as our home(root) route. And the additional functionalities could be linked from there.

The Add and Delete routes are defined as post methods because it is simpler handle these functions as basic form submissions (see the home template).

app.get('/',(req,res) => {

  // retrieve todos from the database
  getTodos((result) => {
    res.render('home',{
      todos: result, // pass it to the template
      empty: result.lenght === 0, //check if to-do list is empty
    });
  });


});

app.post('/add',(req,res)=>{

  // process an add todo request

  console.log("Post recieved!",req.body);
  insertTodo(req.body,() => {
    res.redirect('/'); // after adding redirect to the home page
  });
});

app.post('/delete',(req,res) => {

  // process a delete todo request
  console.log("Post recieved!", req.body);
  deleteTodo(req.body.id,() => { // delete todo by id
    console.log("todo deleted!");
    res.send("Done");
  });
});

  

Finally, we have to tell express to listen for our requests.

app.listen(3000,()=>{
  console.log("Listening on 3000!");
});

The app is complete!  After debugging, try running it.

Run the Application

You can start the app by executing the app.js file.

node app.js

After that go to http://localhost:3000 on your browser. You should see the to-do app. Initially, it will be empty, try adding a to-do and play around with it.

That’s it! You have your first Express App

You can obtain the complete code of this application from the repository here.

Have fun with Express !! 🙂

 

 

 

Haritha Paul